Create multiple incrementing columns using with recursive in postgresql - postgresql

I'm trying to create a table with the following columns:
I want to use a with recursive table to do this. The following code however is giving the following error:
'ERROR: column "b" does not exist'
WITH recursive numbers AS
(
SELECT 1,2,4 AS a, b, c
UNION ALL
SELECT a+1, b+1, c+1
FROM Numbers
WHERE a + 1 <= 10
)
SELECT * FROM numbers;
I'm stuck because when I just include one column this works perfectly. Why is there an error for multiple columns?

This appears to be a simple syntax issue: You are aliasing the columns incorrectly. (SELECT 1,2,4 AS a, b, c) is incorrect. Your attempt has 5 columns: 1,2,a,b,c
Break it down to just: Select 1,2,4 as a,b,c and you see the error but Select 1 a,2 b,4 c works fine.
b is unknown in the base select because it is being interpreted as a field name; yet no table exists having that field. Additionally the union would fail as you have 5 fields in the base and 3 in the recursive union.
DEMO: http://rextester.com/IUWJ67486
One can define the columns outside the select making it easier to manage or change names.
WITH recursive numbers (a,b,c) AS
(
SELECT 1,2,4
UNION ALL
SELECT a+1, b+1, c+1
FROM Numbers
WHERE a + 1 <= 10
)
SELECT * FROM numbers;
or this approach which aliases the fields internally so the 1st select column's names would be used. (a,b,c) vs somereallylongalias... in union query. It should be noted that not only the name of the column originates from the 1st query in the unioned sets; but also the datatype for the column; which, must match between the two queries.
WITH recursive numbers AS
(
SELECT 1 as a ,2 as b,4 as c
UNION ALL
SELECT a+1 someReallyLongAlias
, b+1 someReallyLongAliasAgain
, c+1 someReallyLongAliasYetAgain
FROM Numbers
WHERE a<5
)
SELECT * FROM numbers;
Lastly, If you truly want to stop at 5 then the where clause should be WHERE a < 5. The image depicts this whereas the query does not; so not sure what your end game is here.

Related

Join two tables on all columns to determine if they contain identical information

I want to check if tables table_a and table_b are identical. I thought I could full outer join both tables on all columns and count the number of rows and missing values. However, both tables have many columns and I do not want to explicitly type out every column name.
Both tables have the same number of columns as well as names. How can I full outer join both of them on all columns without explicitly typing every column name?
I would like to do something along this syntax:
select
count(1)
,sum(case when x.id is null then 1 else 0 end) as x_nulls
,sum(case when y.id is null then 1 else 0 end) as y_nulls
from
x
full outer join
y
on
*
;
You can use NATURAL FULL OUTER JOIN here. The NATURAL key word will join on all columns that have the same name.
Just testing if the tables are identical could then be:
SELECT *
FROM x NATURAL FULL OUTER JOIN y
WHERE x.id IS NULL OR y.id IS NULL
This will show "orphaned" rows in either table.
You might use except operators.
For example the following would return an empty set if both tables contain the same rows:
select * from t1
except
select * from t2;
If you want to find rows in t1 that are different to those in t2 you could do
select * from t1
where not exists (select * from t1 except select * from t2);
Provided the number and types of columns match you can use select *, the tables' columns can vary in names; you could also invert the above and union to return combined differences.

Case When Statement - Three columns

Advanced case when statement in T-SQL using three columns
Hope someone can help with the following :)
I have two tables in SQL.
Table 1 has 4 columns - person_id, A, B, and C.
Table 2 has 3 look-up columns - A, B, and C
I want to carry out the following:
Look down Table 1, column A, find value in Table 2, column A
If no value, go to Table 1, column B.
Look down Table 1, column B, find value in Table 2, column B
If no value, go to Table 1, column C
Look down Table 1, column C, find value in Table 2, column C
If no values in any column put 'Null'
So I know how to write a simple case when statement. However, I think that 'case when' only works on one column. I've started to write out the code but need help to get it right.
Select person_id,
case when 1.A is not null then 2.A
when 1.B is not null then 2.B
else 1.C
end as CODE
from table 1
left join table 2
Order by person_id
Would appreciate any help you can give, thank you.
Your sql seems mostly correct, except for the else 1.C.
Since a CASE WHEN will return 1 value, depending on the first positive WHEN criteria.
And a CASE returns NULL as default.
So ELSE null isn't really needed.
The table descriptions do indicate that Table2 only contains 1 row.
If so, then you can cross join them.
SELECT t1.person_id,
CASE
WHEN t1.A IS NOT NULL THEN t2.A
WHEN t1.B IS NOT NULL THEN t2.B
WHEN t1.C IS NOT NULL THEN t2.C
END AS [CODE]
FROM [Table 1] t1
CROSS JOIN (
SELECT TOP 1 A, B, C
FROM [Table 2]
ORDER BY A DESC, B DESC, C DESC
) t2

How to have a custom sort order for a union query in Postgres

With a query like this (simplified for clarity):
SELECT 'East' AS name, *
FROM events
WHERE event_timestamp BETWEEN '2015-06-14 06:15:00' AND '2015-06-21 06:15:00'
UNION
SELECT 'West' AS name, *
FROM events
WHERE event_timestamp BETWEEN '2015-06-14 06:15:00' AND '2015-06-21 06:15:00'
UNION
SELECT 'Both' AS name, *
FROM events
WHERE event_timestamp BETWEEN '2015-06-14 06:15:00' AND '2015-06-21 06:15:00'
I want to customise the order of the resulting rows. Something like:
ORDER BY name='East', name='West', name='Both'
Or
ORDER BY
CASE
WHEN name='East' THEN 1
WHEN name='West' THEN 2
WHEN name='Both' THEN 3
ELSE 4
END;
However, Postgres complains with:
ERROR: invalid UNION/INTERSECT/EXCEPT ORDER BY clause
DETAIL: Only result column names can be used, not expressions or functions.
HINT: Add the expression/function to every SELECT, or move the UNION into a FROM clause.
Do I have any alternative?
Wrap it in a derived table (which is what "HINT: .... or move the UNION into a FROM clause" is suggesting)
select *
from (
... your union goes here ...
) t
order by
CASE
WHEN name='East' THEN 1
WHEN name='West' THEN 2
WHEN name='Both' THEN 3
ELSE 4
END;
I'd add an extra column showing the desired ordering, then use ordinal column positions in the ORDER BY, e.g.
SELECT 1, 'East' AS name, *
...
UNION ALL
SELECT 2, 'West' AS name, *
...
ORDER BY 1
Note that you probably also want UNION ALL since your added columns ensure that every set in the union must be distinct anyway.
By adding an extra column for ordering purpose, however it makes the UNION clause to work exactly as a UNION ALL (it does not eliminate duplicate rows from the result).

PostgreSQL- Adding "TOTAL" row with more than one Group by columns

I was following this topic:
PostgreSQL - making first row show as total of other rows
..and i used this query to accomplish something similar in my code:
with w as ( select fruits, sum(a) a, sum(b) b, sum(c) c
from basket
group by fruits )
select * from w union all select 'total', sum(a), sum(b), sum(c) from w
It works fine but i now need to put two more columns before the sum columns simliar to the fruit one and i'm getting an error :
"... must appear in the GROUP BY clause or be used in an aggregate function"
Any help on how to do like the example above but with to more columns like "fruit"?
(Sorry my rep didn't let me continue the previous topic)
It was an easier fix than i thought.
with w as ( select fruits, vegetables, cereals, sum(a) a, sum(b) b, sum(c) c
from basket
group by fruits, vegetables, cereals )
select * from w union all select 'total', null, null, sum(a), sum(b), sum(c) from w
Two nulls in the last select solved the problem
what you need is something called "Grouping sets". The magic word here is ROLLUP, which is currently not yet supported by PostgreSQL. I guess it will be in 9.5. For now you have to continue on the path you have chosen (= subselects, CTEs, etc.).
a guy called atri shama is currently working on the stuff you really want. sorry for the bad news for now.

Array of possible values in postgresql?

I have the following data:
letter number
A 1
A 2
B 3
B 4
C 5
C 6
How can I write a select statement in Postgres to select this data into a table with an array of all possible numbers for each letter??
letter number array
A {1,2}
B {3,4}
C {5,6}
select letter, array_agg(number) as numbers
from the_table
group by letter;
See also the manual: http://www.postgresql.org/docs/current/static/functions-aggregate.html
If your Postgres version doesn't have "array_agg" function, you can try like this:
select letter,
array(
select number
from the_table t2
where t2.letter = t1.letter
order by number
) as numbers
from the_table t1
group by letter;