Select records not in another table with additional criteria - sql

I am working on an ACCESS DB.
I have 1 table (tblData) with 1 column ( DataId) and 3 entries:
tblData (A)
+--------+
| DataId |
+--------+
| 1 |
| 2 |
| 3 |
+--------+
Another table (tblSelections) contains 3 columns (id, dataid, userid) and has 3 entries:
tblSelections (B)
+----+--------+---------+
| id | dataid | userid |
+----+--------+---------+
| 1 | 1 | 5 |
| 2 | 2 | 5 |
| 3 | 3 | 2 |
+----+--------+---------+
How can I select the records from table A (tblData) which are not in tbl B (tblSelections) for a certain 'userid'?
For 'userid' 5 the query must return 'DataId' 3 from table A as dataid 1 & 2 are already present in table B for userid 5.
For 'userid' 2 the query must return 'DataId' 1 & 2 from table A as dataid 3 is already present in table B for userid 2.
For 'userid' 1 the query must return 'DataId' 1, 2 & 3 from table A as no records are present in table B for userid 1

Use EXISTS or IN for queries like yours:
SELECT *
FROM tblData
WHERE DataId NOT IN
(
SELECT dataid
FROM tblSelections
WHERE userid = 5
);
SELECT *
FROM tblData
WHERE NOT EXISTS
(
SELECT *
FROM tblSelections
WHERE tblSelections.dataid = tblData.DataId AND tblSelections.userid = 5
);

You can use an outer join to select all records, then put a condition in the where clause that a non-nullable column in b is null. This will give you all records in a that do not have a matching row in b according to the join conditions.
This query assumes that you have a parameter or variable named #userid that represents the user ID to search against.
select
a.*
from tblData a
left join tblSelections b on b.dataid = a.dataid and b.userid = #userid
where b.id is null

Related

Count rows from left table without corresponding value in the right table

I want to count rows from left table (of a 1-to-many relation between two tables) that do not have PK-FK representative in right table
Left table
id | value
-----------
1 | a
2 | b
3 | c
Right table
id | id-left | value
--------------------
.. | 1 | ....
the expected result is 2 as rows with id 1 and 3 in left table have no counterpart in right table.
You can use a not exists anti-semi-join:
select count(*)
from l
where not exists (
select * from r where r.id_left = l.id
);

I want to join to a table, but include All records if the table being joined to is a certain ID

I want to join to a table, but include All records if the table being joined to is a certain ID.
I have a list of records with a type_id:
RECORD
id | type_id
---|---
1 | 1
2 | 1
3 | 2
TYPE
id | type_desc
---|---
1 | type1
2 | type2
3 | all
USER
id | type_id
---|---
1 | 1
2 | 3
3 | 2
Record to type is one to one, user to type is one to one, and the "Type" on a record has to be 1 or 2. User can be 1, 2 or 3. The way this would go with a normal join is
select * from record r
inner join user u on u.type_id = r.type_id
where u.user_id=:userId
But now I need to factor in that "All" type, and basically just ignore the join/return all results if the user's type is 3.
So if the user being queried is ID 1, only records 1 and 2 (type 1) would be returned. If userId is 3, only record 3 is returned. But if user ID is 2, corrresponding to the "All" type, then 1,2,3 should be returned.
I hope this helps you:
With dtAll as (
select * from user u
where u.type_id = 3
limit 1
)
select * from record r
inner join user u on u.type_id = r.type_id or exists( select * from dtAll )

Can I join each row of table1 to a unique row of table 2?

I was hoping to query in all the rows of a table that has its ids starting at some number, and update each row of the original table with a one to one of the second table.
For example:
normal
id | fk_test_id
----------------
1 | null
2 | null
3 | null
starts_after
id |
----
12 |
13 |
14 |
What UPDATE can I use to make normal look like this:
id | fk_test_id
----------------
1 | 12
2 | 13
3 | 14
I tried:
UPDATE normal SET fk_test_id = starts_after.id FROM starts_after; which just joins on the first row of starts_after.
UPDATE normal SET fk_test_id = (SELECT id FROM starts_after ORDER BY random() LIMIT 1); Where the subquery only executes once.
Filtering the subquery by which fk_test_ids are already chosen, but it only executes on the pre-updated data.
If you added record with specific order in to starts_after you can use below query:
update normal n
set fk_test_id = tmp.id
from (select id,
row_number() over (order by id)
from starts_after) tmp
where tmp.row_number = n.id;
I ordered by id from starts_after table (ASC) and create range of record with row num:
id | row_number
----------------
12 | 1
13 | 2
14 | 3
After that i join two table and update records

Update records in SQL by looking up in different table

I am copying data from few tables in SQL server A to B. I have a set of staging tables in B and need to update some of those staging tables based on updated values in final target table in B.
Example:
Server B:
StagingTable1:
ID | NAME | CITY
1 ABC XYZ
2 BCD XXX
StagingTable2:
ID | AGE | Table1ID(FK)
10 15 1
20 16 2
After Copying StagingTable1 to TargetTable1 (ID's get auto polulated and I get new ID's, now ID 1 becomes 2 and ID 2 becomes 3)
TargetTable1:
ID | NAME | CITY
1 PQR YYY (pre-existing record)
2 ABC XYZ
3 BCD XXX
So now before I can copy the StagingTable2 I need to update the Table1ID column in it by correct values from TargetTable1.
StagingTable2 should become:
ID | AGE | Table1ID(FK)
10 15 2
20 16 3
I am writing a stored procedure for this and not sure how do I lookup and update the records in staging tables?
Assuming that (name, city) tuples are unique in StagingTable1 and TargetTable1, you can use an updatable common table expression to generate the new mapping and assign the corresponding values:
with cte as (
select st2.Table1ID, tt1.id
from StagingTable2 st2
inner join StagingTable1 st1 on st1.ID = st2.Table1ID
inner join TargetTable1 tt1 on tt1.name = st1.name and tt1.city = st1.city
)
update cte set Table1ID = id
Demo on DB Fiddle - content of StagingTable2 after the update:
id | age | Table1ID
-: | --: | -------:
10 | 15 | 2
20 | 16 | 3

Join Postgresql array to table

I have following tables
create table top100
(
id integer not null,
top100ids integer[] not null
);
create table top100_data
(
id integer not null,
data_string text not null
);
Rows in table top100 look like:
1, {1,2,3,4,5,6...100}
Rows in table top100_data look like:
1, 'string of text, up to 500 chars'
I need to get the text values from table top100_data and join them with table top100.
So the result will be:
1, {'text1','text2','text3',...'text100'}
I am currenly doing this on application side by selecting from top100, then iterating over all array items and then selecting from top100_data and iterating again + transforming ids to their _data text values.
This can be very slow on large data sets.
Is is possible to get this same result with single SQL query?
You can unnest() and re-aggregate:
select t100.id, array_agg(t100d.data order by top100id)
from top100 t100 cross join
unnest(top100ids) as top100id join
top100_data t100d
on t100d.id = top100id
group by t100.id;
Or if you want to keep the original ordering:
select t100.id, array_agg(t100d.data order by top100id.n)
from top100 t100 cross join
unnest(top100ids) with ordinality as top100id(id, n) join
top100_data t100d
on t100d.id = top100id.id
group by t100.id;
Just use unnest and array_agg function in PostgreSQL, your final sql could be like below:
with core as (
select
id,
unnest(top100ids) as top_id
from
top100
)
select
t1.id,
array_agg(t1.data_string) as text_datas
from
top100 t1
join
core c on t1.id = c.top_id
The example of unnest as below:
postgres=# select * from my_test;
id | top_ids
----+--------------
1 | {1,2,3,4,5}
2 | {6,7,8,9,10}
(2 rows)
postgres=# select id, unnest(top_ids) from my_test;
id | unnest
----+--------
1 | 1
1 | 2
1 | 3
1 | 4
1 | 5
2 | 6
2 | 7
2 | 8
2 | 9
2 | 10
(10 rows)
The example of array_agg as below:
postgres=# select * from my_test_1 ;
id | content
----+---------
1 | a
1 | b
1 | c
1 | d
2 | x
2 | y
(6 rows)
postgres=# select id,array_agg(content) from my_test_1 group by id;
id | array_agg
----+-----------
1 | {a,b,c,d}
2 | {x,y}
(2 rows)