How can I trigger a [42P09] error (ambiguous alias) - postgresql

I have tried several ways to trigger a 42P09 (ambiguous alias) error, but none of them succeeded.
This just "works", even when combined with group by x or order by x:
select 1x, 1x;
This also "works":
with t as (select 1x, 1x, 1x) select * from t;
select x from (with x as (select 1x) select x.x x from x group by x order by x) as x;
These only raise a [42712] (duplicate alias):
with t as (select 1x), t as (select 1x) select * from t;
with t as (select 1x, 1x) select * from t, t;
These only raise a [42702] (ambiguous column):
select x from (select 1x, 1x) as t;
with t1 as (select 1x), t2 as (select 1x) select x from t1, t2;
My google-foo didn't bring up any relevant results, either. I could only find mentions of this error existing, but noone seems to encounter it.
Is there any way to trigger a 42P09 error?
Based on #Adrian Klaver's answer, here's a SQL statement that actually yields a 42P09 error:
with t1 as (select 1x), t2 as (select 1y)
select * from t1 x CROSS JOIN
(t2 x CROSS JOIN lateral ( select * from t2 where t1.x = t2.y ) y) z;

If it helps, from source(backend/parser/parse_relation.c):
"
Search the query's table namespace for an RTE matching the
given unqualified refname. Return the RTE if a unique match, or NULL
if no match. Raise error if multiple matches.
Note: it might seem that we shouldn't have to worry about the possibility
of multiple matches; after all, the SQL standard disallows duplicate table
aliases within a given SELECT level. Historically, however, Postgres has
been laxer than that. For example, we allow
SELECT ... FROM tab1 x CROSS JOIN (tab2 x CROSS JOIN tab3 y) z
on the grounds that the aliased join (z) hides the aliases within it,
therefore there is no conflict between the two RTEs named "x". However,
if tab3 is a LATERAL subquery, then from within the subquery both "x"es
are visible. Rather than rejecting queries that used to work, we allow
this situation, and complain only if there's actually an ambiguous
reference to "x".
"

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.

how to get last added record for a battery with left join PSQL

I have query such as
select * from batteries as b ORDER BY inserted_at desc
which gives me data such as
and I have an query such as
select voltage, datetime, battery_id from battery_readings ORDER BY inserted_at desc limit 1
which returns data as
I want to combine both 2 above queries, so in one go, I can have each battery details as well as its last added voltage and datetime from battery_readings.
Postgres has a very useful syntax for this, called DISTINCT ON. This is different from plain DISTINCT in that it keeps only the first row of each set, defined by the sort order. In your case, it would be something like this:
SELECT DISTINCT ON (b.id)
b.id,
b.name,
b.source_url,
b.active,
b.user_id,
b.inserted_at,
b.updated_at,
v.voltage,
v.datetime
FROM battery b
JOIN battery_voltage v ON (b.id = v.battery_id)
ORDER BY b.id, v.datetime desc;
I think that widowing will make what you expected.
Assuming two tables
create table battery (id int, name text);
create table bat_volt(measure_time int, battery_id int, val int);
One of the possible queries is like this:
with latest as (select battery_id, max(measure_time) over (partition by battery_id) from bat_volt)
select * from battery b join bat_volt bv on bv.battery_id=b.id where (b.id,bv.measure_time) in (select * from latest);
If you have Postgres version which supports lateral, it might also make sense to try it out (in case there are way more values than batteries, it could have better performance).
select * from battery b
join bat_volt bv on bv.battery_id=b.id
join lateral
(select battery_id, max(measure_time) over (partition by battery_id) from bat_volt bbv
where bbv.battery_id = b.id limit 1) lbb on (lbb.max = bv.measure_time AND lbb.battery_id = b.id);

Getting error while retrieving data from a sub query and using that data source in another sub query above that

select id,proc_name,p_date,p_no,p_count
from (
(Select id,proc_name,p_date,p_no from aa) x
join
(select id,count(p_no) p_count from aa group by mrn) y
on x.id=y.id
) a
FROM (select id,proc_name,p_date,p_no from zz) aa
getting the error code 42601 at the position of FROM (in upper case).
A SELECT statement can only have one FROM clause.
Instead of using multiple FROM clauses, you should JOIN the tables.
There is no need to have two SELECTs from table aa, you can do that with a single SELECT using window functions:
SELECT id, proc_name, p_date, p_no,
count(p_no) OVER (PARTITION BY mrn) p_count
FROM aa;
You didn't tell over which columns you want to join aa and zz, but your statement could look something like this:
SELECT a.id, a.proc_name, a.p_date, a.p_no, a.p_count
FROM
(SELECT id, proc_name, p_date, p_no,
count(p_no) OVER (PARTITION BY mrn) p_count
FROM aa) a
JOIN zz
ON <join condition for a and zz>;

Postgres - using select statement inside CASE WHEN

Inside case when condition I am executing select statement & if returns anything then I need to get it's value but I am getting error
ERROR: missing FROM-clause entry for table "us"
query is..
SELECT u.user_id,
CASE
WHEN
(SELECT us.attr_value
FROM user_setting us
WHERE us.user_id = u.user_id) IS NOT NULL THEN us.attr_value
ELSE
(SELECT gus.attr_value
FROM global_user_setting gus
WHERE gus.attr_key='key')
END
FROM user u
WHERE u.user_id IN (1,
2,3)
Error comes at IS NOT NULL THEN us.attr_value I understood the issue but couldn't find how to get that value outside select statement?
Try:
COALESCE((SELECT us.attr_value
FROM user_setting us
WHERE us.user_id = u.user_id),
(SELECT us.attr_value
FROM global_user_setting gs
WHERE gus.attr_key='key'))
instead. The reason for the problem is, that the binding of the es alias is not visible outside of the sub-select (as it is used in a "scalar" context). The whole subselect is basically a single expression, which will yield a single value.
Another (IMHO better) approach would be to left-join on the enrollment_settings table:
SELECT u.user_id,
COALESCE(us.attr_value, (SELECT gus.attr_value
FROM global_user_setting gs
WHERE gus.attr_key='key'))
FROM user u LEFT JOIN user_settings es ON us.user_id = u.user_id
WHERE u.user_id IN (1, 2, 3)
I assume here, that this join would yield at most a single row per row of user.

Join postgres table on two columns?

I can't find a straightforward answer. My query is spitting out the wrong result, and I think it's because it's not seeing the "AND" as an actual join.
Can you do something like this and if not, what is the correct approach:
SELECT * from X
LEFT JOIN Y
ON
y.date = x.date AND y.code = x.code
?
This is possible:
The ON clause is the most general kind of join condition: it takes a Boolean value expression of the same kind as is used in a WHERE clause. A pair of rows from T1 and T2 match if the ON expression evaluates to true for them.
http://www.postgresql.org/docs/9.1/static/queries-table-expressions.html#QUERIES-FROM
Your SQL looks OK.
It's fine. In fact, you can put any condition in the ON clause, even one
not related to the key columns or even the the tables at all, eg:
SELECT * from X
LEFT JOIN Y
ON y.date = x.date
AND y.code = x.code
AND EXTRACT (dow from current_date) = 1
Another, arguably more readable way of writing the join is to use tuples of columns:
SELECT * from X
LEFT JOIN Y
ON
(y.date, y.code) = (x.date, x.code)
;
, which clearly indicates that the join is based on the equality on several columns.
This solution has good performance:
select * from(
select md5(concat(date, code)) md5_x from x ) as x1
left join (select md5(concat(date, code)) md5_y from y) as y1
on x1.md5_x = y1.md5_y