To have launchd start postgresql now and restart at login:
brew services start postgresql
Or, if you don't want/need a background service you can just run:
pg_ctl -D /usr/local/var/postgres start
port 5432 is default
psql postgres
psql -d <database_name> -U myUser -W
psql -h <hostname> -p <port_no> -U <db_username> -d <database_name> -W
psql -h ec2-54-225-89-156.compute-1.amazonaws.com -p 5432 -U pmhuletkaeiyep -d d59asl859nm30e -W
\q to quit/exit
\du
postgresql://localhost/my_database
postgresql://localhost/my_database?sslmode=require
DATABASE_URL=postgres://USERNAME:PASSWORD@HOSTNAME:5432/DATABASENAME
Heroku Admin:
heroku pg:psql postgresql-graceful-74509
Better to use Postico (MacOS) or similar instead.
Tip: table names as singular (Company, Person) – https://stackoverflow.com/a/5841297/449227
e.g. WHERE person.company_id = company.id
First psql postgres
, then:
CREATE DATABASE my_database;
Optional:
GRANT ALL PRIVILEGES ON DATABASE my_database TO yourusername;
\connect my_database
\list my_database
\connect: connect to database
List databases
\list
Delete database
DROP DATABASE my_database;
ALTER DATABASE "db" RENAME TO "newdb";
\dt
List all:
\dt *.*
List users:
\du
Current user:
SELECT current_user;
Create user
CREATE USER username [WITH PASSWORD 'password'];
serial
: same asint
(use for relations) except that PostgreSQL will automatically generate and populate values.- Text:
char(n)
: paddedvarchar(n)
text
- Numbers:
smallint
: 2-byte signed integer that has a range from -32,768 to 32,767.int
: a 4-byte integer that has a range from -2,147,483,648 to -2,147,483,647.real
or float8: double-precision (8-byte) floating-point number.float(n)
: floating-point number whose precision, at least, n, up to a maximum of 8 bytes.numeric
ornumeric(p,s)
: real number with p digits with s number after the decimal point. Thenumeric(p,s)
is the exact number.
boolean
: true/false, t/f- Time & Date:
timestamptz
: (DEFAULT now()
) timezone-aware timestamp data type. It is the abbreviation for timestamp with time zone. PostgreSQL’s extensiontimestamp
: stores both date and time values.date
: stores the date values only.time
: stores the time of day values.interval
: stores periods of time.
- Binary & JSON:
bytea
: blob binaryjson
, jsonb
COPY domain_update TO './data/domain_update.csv' DELIMITER ',' CSV HEADER;
https://www.npmjs.com/package/pg
NOTE: if you have connection timeout issues, try upgrading pg
:
yarn remove pg; yarn add pg
Simple client:
const { Client } = require('pg')
const postgresOptions = {
connectionString: config.databaseUrl,
// max: 10, // default 10
// connectionTimeoutMillis: 1000, // default 0 = no timeout
// idleTimeoutMillis: 1000, // default 10000 ms
// ssl: { rejectUnauthorized: false },
}
// const results = await runDatabaseFunction(async (client) => client.query(sqlString))
const runDatabaseFunction = async function (functionToRun) {
// Connect db
const client = new Client(postgresOptions)
await client.connect()
// Run function
const { rows } = await client.query(sqlString)
// OR: const results = await functionToRun(client)
// Release db
await client.end()
return results
}
With connection pooling:
const { Pool } = require('pg')
const pool = new Pool(postgresOptions)
// const results = await runDatabaseFunction(async (client) => client.query(sqlString))
module.exports.runDatabaseFunction = async function (functionToRun) {
// Connect db
const client = await pool.connect()
// Run function
const { rows } = await client.query(sqlString)
// OR: const results = await functionToRun(client)
// Release db
await client.end() // used?
await client.release()
return results
}
https://stackoverflow.com/questions/21759852/easier-way-to-update-data-with-node-postgres
Select:
const getCompany = async function (pool, req, res, next) {
const sqlString = `SELECT * FROM company WHERE id = $1;`
const { rows } = await pool.query(sqlString, [req.params.id])
res.json(rows[0])
}
Insert:
// sqlCreate('person', { person values... })
const sqlCreate = (tableName, object) => {
const fieldNames = Object.keys(object).join(', ')
const fieldCounters = Object.keys(object).map((fieldName, index) => `$${index + 1}`).join(', ')
const text = `INSERT INTO ${tableName}(${fieldNames}) VALUES(${fieldCounters})`
const values = Object.values(object)
return { text, values }
}
Update:
// sqlUpdate('person', { id: person.id }, { person values... })
const sqlUpdate = (tableName, query, newValues) => {
const fieldDefinitions = Object.keys(newValues).map((fieldName, index) => `${fieldName} = ($${index + 2})`).join(', ')
const queryFieldName = Object.keys(query)[0]
const text = `UPDATE ${tableName} SET ${fieldDefinitions} WHERE ${queryFieldName}=($1)`
const values = [Object.values(query)[0], ...Object.values(newValues)]
return { text, values }
}
SELECT * FROM table WHERE id = 123;
const sqlString = `SELECT
font.id, font.name,
category.name AS category_name,
source.name AS source_name,
creator.name AS creator_name
FROM font
LEFT JOIN category_font ON (category_font.font_id = font.id)
LEFT JOIN category ON (category.id = category_font.category_id)
LEFT JOIN source ON (source.id = font.source_id)
LEFT JOIN creator ON (creator.id = font.creator_id)
WHERE true
${name ? `AND font.name ILIKE '${name}%'` : ''}
${category ? `AND category.name = '${category}'` : ''}
${weight ? `AND font.variants ILIKE '%${weight}%'` : ''}
${source ? `AND source.name = '${source}'` : ''}
LIMIT 100
;`
SELECT * FROM table WHERE columnName ILIKE 'A%';
SELECT * FROM company WHERE name ILIKE '%weld%';
SELECT * FROM company WHERE website NOT ILIKE 'http%'
Tip: you can use LOWER()
for lowercase formatting.
SELECT * FROM person WHERE contact_status_date < '2019-02-09';
SELECT * FROM updates WHERE date_update BETWEEN '2019-01-05' AND '2019-01-10';
SELECT * FROM books WHERE returned_date > (CURRENT_DATE - INTERVAL '7 days');
SELECT * FROM users WHERE created_at < (CURRENT_DATE - INTERVAL '7 days');
SELECT ROUND(your_column::numeric, 2) AS rounded_value FROM your_table;
SELECT * FROM company WHERE name IN ('a', 'steeple', 'the');
SELECT * FROM document WHERE accessing_user_ids @> ARRAY['4601ab73-d742-429e-b8e3-ba349725e5f5']::uuid[]
SELECT * FROM company LIMIT 10 OFFSET 30;
ORDER BY field_name ASC/DESC NULLS FIRST/LAST
SELECT COALESCE(phone_number, 'No Phone') AS phone_number FROM employees;
JOIN Type | Includes Matching Rows (from both tables) | Includes Unmatched Rows (Left Table) | Includes Unmatched Rows (Right Table) |
---|---|---|---|
INNER JOIN (or just JOIN) | ✅ Yes | ❌ No | ❌ No |
LEFT JOIN | ✅ Yes | ✅ Yes | ❌ No |
RIGHT JOIN | ✅ Yes | ❌ No | ✅ Yes |
FULL OUTER JOIN | ✅ Yes | ✅ Yes | ✅ Yes |
Examples:
SELECT * FROM weather
LEFT JOIN city ON (weather.city = city.name);
SELECT company.id, company.name, person.name
FROM company
LEFT JOIN company_person ON (company_person.company_id = company.id)
LEFT JOIN person ON (person.id = company_person.person_id)
ORDER BY company.name;
Note: LEFT
refers to the left table in ON
statement:
SELECT * FROM weather LEFT OUTER JOIN city ON (weather.city = city.name);
SELECT domain.id, name, avg(sai) AS avg_sai FROM domain LEFT OUTER JOIN domain_update ON (domain.id = domain_update.domain_id) GROUP BY domain.id;
Lateral join:
SELECT company.id, company.name, person.id, person.name
FROM company
LEFT JOIN LATERAL (
SELECT id, name
FROM person
WHERE company_id=company.id
LIMIT 1
) person ON TRUE;
CASE
WHEN condition1 THEN result1
WHEN conditionN THEN resultN
ELSE result
END;
as part of SELECT
:
(CASE WHEN user.id IS NOT NULL THEN user_profile.name ELSE null END) AS user_name
(CASE WHEN username IS NOT NULL THEN username ELSE CAST(article.user_id AS varchar) END) AS user
INITCAP(CASE WHEN product.name ILIKE brand.name || '%' THEN product.name ELSE CONCAT(brand.name, ' ', product.name) END) AS product_name
Update with RANDOM()
:
UPDATE "order" SET owner_id = (CASE WHEN RANDOM() < 0.5 THEN 21 ELSE 12 END);
SELECT * FROM (
SELECT DISTINCT ON (person.id)
person.*,
title
FROM person
) subquery
WHERE title='CEO';
Note: column types must match in the same order.
SELECT * FROM (
(
SELECT font.id, name, slug, category_id
FROM similar_font
LEFT JOIN font ON (similar_font.font2_id = font.id)
WHERE font1_id = 1643
)
UNION
(
SELECT font.id, name, slug, category_id
FROM similar_font
LEFT JOIN font ON (similar_font.font1_id = font.id)
WHERE font2_id = 1643
)
) AS combined_query
ORDER BY name;
COUNT(DISTINCT(field))
Example:
SELECT
company.*,
COUNT(DISTINCT(company_person.person_id)) AS person_count
FROM company
LEFT JOIN company_person ON (company_person.company_id = company.id)
GROUP BY company.id;
SELECT REPLACE(column_name, 'search_string', 'replace_string') FROM table_name;
STRING_AGG(DISTINCT(category.name), ',') AS category_names
Simpler concatenation
SELECT CONCAT('SQL', ' is', ' fun!') as longer_string;
ARRAY_AGG(category.name) AS category_names
ARRAY_AGG(DISTINCT(category.name)) AS unique_category_names
WITH RECURSIVE category_tree AS (
-- Anchor member: starting point
SELECT id, name, parent_category_id
FROM category
WHERE id = 22 -- <- Change starting category ID here
UNION ALL
-- Recursive member: find parent categories recursively
SELECT c.id, c.name, c.parent_category_id
FROM category c
JOIN category_tree ct ON c.id = ct.parent_category_id
)
-- Select all parent categories, including the starting point
SELECT * FROM category_tree;
SELECT
TO_CHAR(created_date, 'IW') AS period_name,
COUNT(*) AS total_orders
FROM
"order"
WHERE
(selected_owner_id IS NULL OR selected_owner_id = "order".owner_id)
AND created_date >= start_date
AND created_date <= end_date
GROUP BY
period_name
ORDER BY
period_name;
INSERT INTO domain (name, site_count)
VALUES ('indiska.se', 5) RETURNING id;
Multiple values:
INSERT INTO domain (name, count)
VALUES ('domain1.se', 2), ('domain2.se', 3);
INSERT INTO "${tableName}" (${keyFieldNames}, ${otherFieldNames.join(', ')})
VALUES (${allFieldValues.join(', ')})
ON CONFLICT (${keyFieldNames})
DO UPDATE SET ${otherFieldNames.map(fieldName => `${fieldName} = EXCLUDED.${fieldName}`).join(', ')};
UPDATE domain
SET
content_last_update = '2019-01-01 12:00',
content_previous_update = null
WHERE id = 1;
DELETE FROM domain WHERE name = 'formomiljo.se';
SELECT
CONCAT(tables.table_schema, '.', tables.table_name) as schema_and_table
FROM information_schema.tables tables
WHERE
tables.table_type = 'BASE TABLE' -- Exclude views
AND tables.table_schema IN ('public', 'basejump', 'auth')
-- AND tables.table_name ILIKE 'account%' -- NOTE: change here to see other tables
ORDER BY tables.table_schema ASC, tables.table_name ASC;
With columns:
SELECT
CONCAT(columns.table_schema, '.', columns.table_name) as schema_and_table,
columns.column_name,
columns.data_type as column_data_type
-- columns.character_maximum_length,
-- columns.is_nullable,
-- columns.column_default
FROM information_schema.columns columns
JOIN information_schema.tables tables
ON columns.table_name = tables.table_name
AND columns.table_schema = tables.table_schema
WHERE tables.table_type = 'BASE TABLE' -- Exclude views
AND columns.table_schema IN ('public', 'basejump', 'auth')
-- AND columns.table_name ILIKE 'account%' -- NOTE: change here to see other tables
ORDER BY columns.table_schema DESC, columns.table_name ASC;
CREATE TABLE person (
id serial,
name varchar(128),
date_created timestamptz DEFAULT now(),
PRIMARY KEY (id),
UNIQUE (name)
);
CREATE TABLE company_person (
company_id integer REFERENCES "company"(id) ON DELETE CASCADE,
person_id integer REFERENCES "person"(id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX company_person_unique_idx ON company_person(company_id, person_id);
TYPE/ENUM:
CREATE TYPE interval_type AS ENUM ('monthly', 'quarterly', 'biannual', 'annual');
CREATE TABLE my_table (
interval interval_type NOT NULL
);
DOMAIN/CHECK:
CREATE DOMAIN valid_period_months AS integer CHECK (VALUE IN (1, 3, 6, 12));
CREATE TABLE my_table (
period valid_period_months
);
CREATE TABLE flexi_date (
id SERIAL PRIMARY KEY,
year SMALLINT NOT NULL CHECK (year >= 1970 AND year <= 2300),
month SMALLINT CHECK (month >= 1 AND month <= 12),
day SMALLINT CHECK (day >= 1 AND day <= 31),
quarter SMALLINT CHECK (quarter >= 1 AND quarter <= 4)
);
ALTER TABLE person
ADD COLUMN company_id integer NOT NULL REFERENCES company(id) ON DELETE CASCADE;
Remove:
ALTER TABLE domain
DROP COLUMN "change_fraction";
DROP TABLE person;
If delete Company, then delete Person too:
CREATE TABLE company(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE person(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
company_id INT NOT NULL,
CONSTRAINT fk_person_company_id FOREIGN KEY (company_id) REFERENCES company (id) ON DELETE CASCADE
);
Modify existing table:
ALTER TABLE "domain_update" ADD FOREIGN KEY ("domain_update_domain_id_fkey") REFERENCES "domain"("id") ON DELETE CASCADE;
ALTER TABLE person ADD CONSTRAINT fk_person_company_id FOREIGN KEY (company_id) REFERENCES company (id) ON DELETE CASCADE;
CREATE UNIQUE INDEX event_unique_uuid ON event (uuid);
CREATE UNIQUE INDEX company_person_unique_idx ON company_person(company_id, person_id);
CREATE [MATERIALIZED] VIEW my_view AS (
your query here
)
CREATE OR REPLACE VIEW my_view AS (
your query here
)
Refresh materialized view (view with stored results):
REFRESH MATERIALIZED VIEW my_view
Delete view:
DROP [MATERIALIZED] VIEW IF EXISTS my_view;
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
In SQL:
CREATE OR REPLACE FUNCTION my_function(name text)
RETURNS TEXT
LANGUAGE plpgsql -- or 'sql'
AS $$
SELECT 'Hello World!', name;
$$;
Executing the function from SQL:
SELECT * FROM my_function('Sam Lowry');
View a function:
SELECT pg_get_functiondef('my_function'::regproc);
In psql
: \df+ my_function
List all functions:
SELECT
routines.routine_name AS name,
ARRAY_AGG(parameters.parameter_name) AS parameter_names,
ARRAY_AGG(parameters.data_type) AS parameter_types,
routines.data_type AS return_type
FROM information_schema.routines
LEFT JOIN information_schema.parameters ON (routines.specific_name = parameters.specific_name)
WHERE routine_type = 'FUNCTION' AND routine_schema = 'public'
-- AND routines.routine_name ILIKE '%order%'
AND parameters.parameter_mode = 'IN'
GROUP BY routines.routine_name, routines.data_type
ORDER BY routines.routine_name;
Delete a function:
DROP FUNCTION my_function;
Example: return SETOF
CREATE OR REPLACE FUNCTION get_planets()
RETURNS SETOF planets
LANGUAGE plpgsql
AS $$
SELECT * FROM planets;
$$;
Example with custom RETURNS table
definition:
CREATE OR REPLACE FUNCTION public.get_order(_orderid integer)
RETURNS table (
orderid integer,
transportername text,
orderednumberofitems decimal
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN query
SELECT
orderid,
transportername,
orderednumberofitems
FROM orders
WHERE orderid = _orderid;
END;
$$;
Example: add_geometry
:
CREATE OR REPLACE FUNCTION add_geometry (location_name varchar, lon float, lat float)
RETURNS SETOF geometries
LANGUAGE plpgsql
AS $
DECLARE
return_record geometries%rowtype;
BEGIN
INSERT INTO geometries(location_name, geom)
VALUES (location_name, ST_SETSRID(ST_MAKEPOINT(lon, lat), 4326))
RETURNING *
INTO return_record;
RETURN NEXT return_record;
END
$;
Example: app.id_generator
:
CREATE OR REPLACE FUNCTION app.id_generator(OUT result bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $$
DECLARE
our_epoch bigint := 1111111111111;
seq_id bigint;
now_millis bigint;
-- the id of this DB shard, must be set for each
-- schema shard you have - you could pass this as a parameter too
shard_id int := 1;
BEGIN
SELECT nextval('app.global_id_sequence') % 1024 INTO seq_id;
SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
result := (now_millis - our_epoch) << 23;
result := result | (shard_id << 10);
result := result | (seq_id);
END;
$$;
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public';
Move to another schema:
ALTER TABLE public.my_table
SET SCHEMA company.departments;
SELECT current_user;
See permissions:
SELECT table_name, grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'my_table';
Grant permissions:
GRANT DELETE ON TABLE my_table TO anon;
SELECT relname, relrowsecurity
FROM pg_class
WHERE relname = 'my_table';
pg_dump mydatabase > mydatabase_dump.sql
PGPASSWORD="your_password" pg_dump -U username -h localhost -p 5432 -t public.tablename mydatabase > tablename_dump.sql
pg_dump --no-owner --no-acl --data-only --inserts -d mydatabase -t public.tablename > tablename_dump.sql
One table INSERT:
pg_dump -h localhost -p 54332 -U postgres -d postgres -t tablename -F p --column-inserts -f tablename_dump.sql
Restore:
psql -U [username] -h [hostname] -p [port] -d [destination_database] < [dumpfile.sql]
mysql -p -e "CREATE DATABASE yourdatabase;"
mysql -u username -p -e "CREATE DATABASE yourdatabase;"
mysql -u username -p yourdatabase < mydatabase_dump.sql
ALTER TABLE company_person
ADD CONSTRAINT unique_company_person UNIQUE (company_id, person_id);
SHOW CREATE VIEW view_name;
CREATE OR REPLACE VIEW view_name AS
SELECT...
.exit
http://www.sqlite.org/datatype3.html
Each value stored in an SQLite database (or manipulated by the database engine) has one of the following storage classes:
NULL
: The value is a NULL value.INTEGER
: The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.REAL
: The value is a floating point value, stored as an 8-byte IEEE floating point number.TEXT
: The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).BLOB
: The value is a blob of data, stored exactly as it was input.
.schema tracks
.show
.separator , // or \t
.import top_30.csv tracks
.dump ?TABLE? ...
: Dump the database in an SQL text format
.import FILE TABLE
: Import data from FILE into TABLE