Inserting Data from a Union Query into New Table - postgresql

Using SQL in DBeaver 22.2.3. I have data that is in different tables (with the same columns just different values). I used a Union statement to join all the data in my return. However, how can I move the data from that return into a new table (to combine all the data into one) so only certain columns are in it?
The code I tried was:
insert geom, ccn, report_dat, shift, "method", offense, ward, district, latitude, longitude, start_date, end_date
into normal
from c, c2, c3, c4, c5, c6;
What I expected was the data from all the tables would be in the table "normal", I had found the code on StackOverflow and applied it to my data (it did not work).

I suggest CREATE TABLE AS. And UNION ALL (unless you want to remove duplicates):
CREATE TABLE new_table AS
SELECT geom, ccn, report_dat, shift, method, offense, ward, district, latitude, longitude, start_date, end_date
FROM c
UNION ALL
SELECT geom, ccn, report_dat, shift, method, offense, ward, district, latitude, longitude, start_date, end_date
FROM c2
UNION ALL ...

Related

Using a parameter for the function to call

Within PostgresSQL how can I define a parameter as a function, i.e.
SELECT MAX(value) FROM table;
SELECT MIN(value) FROM table;
SELECT AVG(value) FROM table;
where the function is a parameter, something like the following:
SELECT $1(value) FROM table;
You cannot do that. Use multiple separate queries, and choose the one to execute. If you have to do this within SQL, you can also do the choosing depending on a parameter:
SELECT (CASE $1::text
WHEN 'max' THEN (SELECT MAX(value) FROM table)
WHEN 'min' THEN (SELECT MIN(value) FROM table)
WHEN 'avg' THEN (SELECT AVG(value) FROM table)
END);

add Exists operator in a union sql query

I have two tables that am using union to combine the result-set of them my problem here is Each SELECT statement within UNION must have the same number of columns and data types, I don't have the same number of columns so am creating null columns
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
coalesce('No_user'::text,'0'::text) as IsUser, --i get an error :UNION types boolean and character varying cannot be matched
from table2 o
how can I create a null column in table2 that matches the boolean data type of the table1
how can I create a null column in table2 that matches the boolean data type of the table1
By putting NULL into the SELECT list of the second query.
Generally (in SQL) the datatype for the column is dictated by the first query in the union set and other queries must match. You don't need column aliases for subsequent queries either, but they may help make a query more readable:
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
null --IsUser
from table2 o
If you have a case where the type of the column in the first query is different to what you want in the output, you cast the first column:
select 1::boolean as boolcol
UNION
select true
This will ensure that the column is boolean, rather than giving a "integer and bool cannot be matched". Remember also that, should you need to, NULL can be cast to a type, e.g. select null::boolean as bolcol

The "order by" in a function in postgresql is reversed when called slightly different

I have a function that select from a table where transactions are stored. The function is longer so I have made a terse version to try to illustrate the issue I have. The original function have a "union all" to get total sum, sum by division and finally by product which I have left out. The output is converted to a PDF-file. The behaviour I see does not seem to be caused by the function itself but when I call the function in two ways.
I join a customer table using a organisation-id. Some customers are registered more than once with the same organisation-id but different customer-ids. There are reasons beyond my control why a customer can be registered more than once. So in order not to get duplicate lines I made an exemption-table for these cases.
I then "order by" using a case since I need to display the products in a specific order. With the first call the products are sorted as desired but the latter the sort is reversed.
This is on ubuntu 18.04 LTS and postgresql 10.12.
create or replace function f(quarter integer, year integer, cid text)
returns table (cid text, name text, product text, quantity numeric, vat text, amount numeric)
as
$body$
select
cid,
name,
internal_product,
sum(quantity) as quantity,
vat,
sum(amount) as amount
from transaction
where
extract('quarter' from created_at) = $1 and extract('year' from created_at) = $2
group by
cid,
name,
internal_product,
vat) as VAT
where cid in ($3)
order by
name desc,
case internal_product
when 'product A' then 1
when 'extra B' then 2
when 'large C' then 3
when 'small D' then 4
when '' then 99
end
$body$
language sql;
This sorts the way I want.
select
c.cid, c.cidtext, f.*
from
f(2,2020,'958935420') f
join
customer c
on
f.cid = c.cid;
Adding the exemption table reverses the sort.
select
c.cid, c.cidtext, f.*
from
f(2,2020,'958935420') f
join
customer c
on
f.cid = c.cid
where c.cid not in (select cid from exempt_cid);
A SELECT statement only has a definite ordering if it has an ORDER BY clause. Since the statement that calls the function doesn't have ORDER BY, you don't have that guarantee.
In practice, it will depend on the execution plan (use EXPLAIN). The function itself will return values in the desired order, and if there is nothing in the rest of the execution plan that disturbs that order, you are fine.
My advice is never to add an ORDER BY clause to a view definition or a function result, but always have it where it belongs, in the calling query. PostgreSQL doesn't optimize such ORDER BY clauses, and you may end up paying the price of a sort without having any benefit.
If you are ordering in the function to simplify code maintenance in case the ordering has to change in the future and you do not want to update multiple calling queries:
create or replace function f(quarter integer, year integer, cid text)
returns table (cid text, name text, product text, quantity numeric, vat text, amount numeric)
as
$body$
select
cid,
name,
internal_product,
sum(quantity) as quantity,
vat,
sum(amount) as amount,
row_number()
over (order by name desc,
case internal_product
when 'product A' then 1
when 'extra B' then 2
when 'large C' then 3
when 'small D' then 4
when '' then 99
end) as sort_key
from transaction
where
extract('quarter' from created_at) = $1 and extract('year' from created_at) = $2
group by
cid,
name,
internal_product,
vat) as VAT
where cid in ($3)
$body$
language sql;
Then your calls would look like this:
select
c.cid, c.cidtext, f.cid, f.name, f.internal_product, f.quantity, f.vat, f.amount
from
f(2,2020,'958935420') f
join
customer c
on
f.cid = c.cid
order by sort_key;

PostgreSQL - Append a table to another and add a field without listing all fields

I have two tables:
table_a with fields item_id,rank, and 50 other fields.
table_b with fields item_id, and the same 50 fields as table_a
I need to write a SELECT query that adds the rows of table_b to table_a but with rank set to a specific value, let's say 4.
Currently I have:
SELECT * FROM table_a
UNION
SELECT item_id, 4 rank, field_1, field_2, ...
How can I join the two tables together without writing out all of the fields and without using an INSERT query?
EDIT:
My idea is to join table_b to table_a somehow with the rank field remaining empty, then simply replace the null rank fields. The rank field is never null, but item_id can be duplicated and table_a may have item_id values that are not in table_b, and vice-versa.
I am not sure I understand why you need this, but you can use jsonb functions:
select (jsonb_populate_record(null::table_a, row)).*
from (
select to_jsonb(a) as row
from table_a a
union
select to_jsonb(b) || '{"rank": 4}'
from table_b b
) s
order by item_id;
Working example in rextester.
I'm pretty sure I've got it. The predefined rank column can be inserted into table_b by joining to the subset of itself with only the columns left of the column behind which you want to insert.
WITH
_leftcols AS ( SELECT item_id, 4 rank FROM table_b ),
_combined AS ( SELECT * FROM table_b JOIN _leftcols USING (item_id) )
SELECT * FROM _combined
UNION
SELECT * FROM table_a

PostgreSQL - How to get distinct on two columns separately?

I've a table like this:
Source table "tab"
column1 column2
x 1
x 2
y 1
y 2
y 3
z 3
How can I build the query to get result with unique values in each of two columns separately. For example I'd like to get a result like one of these sets:
column1 column2
x 1
y 2
z 3
or
column1 column2
x 2
y 1
z 3
or ...
Thanks.
What you're asking for is difficult because it's weird: SQL treats rows as related fields but you're asking to make two separate lists (distinct values from col1 and distinct values from col2) then display them in one output table not caring how the rows match up.
You can so this by writing the SQL along those lines. Write a separate select distinct for each column, then put them together somehow. I'd put them together by giving each row in each results a row number, then joining them both to a big list of numbers.
It's not clear what you want null to mean. Does it mean there's a null in one of the columns, or that there's not the same number of distinct values in each column? This one problem from asking for things that don't match up with typical relational logic.
Here's an example, removing the null value from the data since that confuses the issue, different data values to avoid confusing rowNumber with data and so there are 3 distinct values in one column and 4 in another. This works for SQL Server, presumably there's a variation for PostgreSQL.
if object_id('mytable') is not null drop table mytable;
create table mytable ( col1 nvarchar(10) null, col2 nvarchar(10) null)
insert into mytable
select 'x', 'a'
union all select 'x', 'b'
union all select 'y', 'c'
union all select 'y', 'b'
union all select 'y', 'd'
union all select 'z', 'a'
select c1.col1, c2.col2
from
-- derived table giving distinct values of col1 and a rownumber column
( select col1
, row_number() over (order by col1) as rowNumber
from ( select distinct col1 from mytable ) x ) as c1
full outer join
-- derived table giving distinct values of col2 and a rownumber column
( select col2
, row_number() over (order by col2) as rowNumber
from ( select distinct col2 from mytable ) x ) as c2
on c1.rowNumber = c2.rowNumber