IN with more than one column is possible? - firebird

I have this SQL
Select A, B, D from T
Where not exists (select F, S from Z)
Can I do the same thing using NOT IN for more than one column ?

Some databases (eg postgresql) have support for row values, where you can do something like ROW(x, y) NOT IN (select f, s from z), Firebird unfortunately does not have row values, so you cannot have more than one column in an IN (or NOT IN).
However you can usually emulate it with a correlated subquery in the exists, eg:
SELECT A, B, D
FROM T
WHERE NOT EXISTS (
SELECT 1
FROM Z
WHERE Z.F = T.X AND Z.S = T.Y
)
Note that EXISTS doesn't care about the selected values, but just that one or more rows were produced, so the two columns you use in your select within the EXISTS are not relevant (it could just as well have been 1 as I used above).

Related

JPA equivalent of PostgreSQL with null comparison

I have a requirement something like there are two db tables A and B.
Table A and table B both have column 'merit' of type integer. The requirement is to find the entries from table A those have merit that matches with the least merit number in table B.
If merit in B is NULL for all the entries, the query should result all the entries from table A.
If merit in B has valid numbers, the query should result the entries of A those match the least merit number in table B.
Sample data is like this::
TABLE A TABLE B
COL1 COL2 MERIT COL1 COL2 MERIT
a ab 1 c ac 1
b bc 2 d ad 3
From above data the least merit in B is 1 so, only the matching entry should result from Table A.
If merit column in B is null for all the entries ie. B has no valid number for merit, two entries from A should result.
So, I came up with the below sql query::
select A.* from A where A.merit IS NOT DISTINCT FROM (
select min(B.merit) from B where B.merit IS NOT NULL);
I am unable to write the JPA equivalent of this sql because of "IS NOT DISTINCT FROM".
The below queries are not working.
select a from A a where a.merit in (select min(b.merit) from B b where b.merit is not null)
select a from A a where a.merit = (select min(b.merit) from B b where b.merit is not null)
My environment is POSTGRESQL, HIBERNATE in QUARKUS.
Thanks for any suggestions.

Postgres - Insert nearest neighbour distance into another table

So I have three tables (A, B, C). In tables A and B I have points, and I want to insert into C each row from A, and some columns from the closest point from B to each point in A, as well as the distance between them. I know that the query to get the nearest neighbour is this:
SELECT DISTINCT ON (A.id5) A.state, B.way, st_distance (A.geom,B.geom) INTO C
FROM A, B
WHERE ST_DWithin(A.geom, B.geom, 150)
ORDER BY A.objectid, ST_Distance(A.geom,A.geom)
But I need to get that into a bigger INSERT query, and I tried to do it this way:
INSERT INTO complete(id_door, distance, id_way,Y, X, geom, check)
(SELECT A.state, (select distinct on (A.id5) ST_DISTANCE(A.geom,B.geom) from A order by A.id5, st_distance(A.geom,B.geom)), b.way, ST_Y(B.geom), ST_X(B.geom) ,B.geom, V.check
FROM A, B, C, V
WHERE
ST_INTERSECTS(A.geom, V.geom)\
AND ST_DWithin(A.geom, B.geom,150))
But this is not the right way, because I get the error:
psycopg2.ProgrammingError: more than one row returned by a subquery used as an expression
I cannot copy all the distances from A and B to C and then delete all but the closest because it is a huge table and I would run out of memory, so I need a way to only insert the rows with the info from the closest point from B to A.
What am I doing wrong here? Thank you in advance
UPDATE:
After some help, I have learned that I should use a Lateral in the Select query, but I'm not sure how to use it.
I need the Select to get each row in table A and find its nearest neighbour from table B, which I guess it is done using the query previously stated, and insert into table C some columns from A, some columns from its nearest neighbour (table B), and some columns from table V, which is selected by an Intersect condition. The main problem is how to organize all that into the Select so I don't get an error.
This is where I am at this point:
INSERT INTO C (id_door, distance, id_way,Y, X, geom, check)
(SELECT A.state, l.*, V.check
FROM A, B, C, V
lateral (select st_distance(a.geom,b.geom), b.way, ST_Y(B.geom), ST_X(B.geom) ,B.geom
From B
Where ST_DWithin(a.geom, b.geom,150))
Order by a.geom<->b.geom limit 1) l
WHERE
ST_INTERSECTS(A.geom, V.geom)
You can use lateral join - very smart type of subquery that can reference tables outside the subquery. More about lateral you can find here
-- Edited according to new information in answer --
Insert into C (id_door, distance, id_way,Y, X, geom, check)
select l.*
from a,
lateral (select a.state, st_distance(a.geom,b.geom),
b.way, ST_Y(B.geom), ST_X(B.geom), B.geom,
v.check
from b, v
where ST_DWithin(a.geom, b.geom,150)
and st_dwithin(a.geom,v.geom,0)
and st_intersects(a.geom,v.geom)
order by a.geom<->b.geom, v.geom limit 1) l
If you want more records per each point from A then increase the limit from 1 to your desired value.

Create multiple incrementing columns using with recursive in 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.

Add a new column dynamically in resultset

I have a query as below;
Existing Query - select A,B,C from table1.
Table2 has columns X,Y
The new query should have a new column(D) in the result-set. the value of D will be calculated based on column X.
D's calculation should be D = (C * X), Here to decide the row of column X from table2 -Y can be used in where condition. Y & A are not same but similar
I did not understand what did you mean by "Y & A are not same but similar". I assume Y and A can be used as joining keys. If so, the anwer would be:
SELECT T1.A,T1.B,T1.C,T1.C*T2.X AS D
FROM Table1 T1
JOIN Table2 T2 ON T1.A=T2.Y
I hope this helps!

Get field value from a record that causes an aggregate condition to be true

I have a table x which have the fields a, b, c, and d. I want to do a SELECT statement which is GROUPED BY a HAVING a_particular_value = ANY(array_agg(b)) and retrieves a, MIN(d), and c <- from which row is chosen by a_particular_value = ANY(array_agg(b)).
It's a bit confusing.
Lemme try to explain. a_particular_value = ANY(array_agg(b)) will choose some or one record from all records that is grouped by a. I want to retrieve the value of c from the record that causes the condition to be true. While NOT filter out other records because I still need those for the other aggregate function, MIN(d).
The query that I've tried to make:
SELECT a, MIN(d) FROM x
GROUP BY a
HAVING 1 = ANY(array_agg(b))
The only thing that's left to do is put c in the SELECT clause. How do I do this?
with agg as (
select a, min(d) as d
from x
group by a
having 1 = any(array_agg(b))
)
select distinct on (a, c)
a, c, d
from
x
inner join
agg using (a, d)
order by a, c
If min(d) is not unique within the a group then it is possible to exist more than one corresponding c. The above will return the smallest c. If you want the biggest do in instead
order by a, c desc
c can have various values in this scenario, so your only option is to group by c as well.
SELECT a, c FROM x
GROUP BY a, c
HAVING 1 = ANY(array_agg(b))
If you want to eliminate rows with b not satisfying condition before applying GROUP BY then use WHERE as documentation for HAVING says http://www.postgresql.org/docs/9.2/static/sql-select.html#SQL-HAVING