Join Table with Unnested Array in postgres - postgresql

I have a table of contacts and a study to with which they are associated. Now I am being passed an array of integers, representing contact_id. I need to join the current table of contacts with an unnested array of ids and delete the record if the unnested integer matches contact_id.
I have this so far
SELECT cvs.contact_id, cvs.study_id
FROM study.contacts_vs_studies AS cvs
LEFT JOIN (SELECT UNNEST('{1,2,3,4,68}'::INT[])) AS old_ids
ON cvs.contact_id = old_ids.old_ids
WHERE study_id = 6
This just returns all the available contacts for the study and nothing from the unnested array.
How can get a result of unnested array that matches select from existing table? TY

I think that your query does what you want. But you cannot see it since you are doing a left join and not selecting anything from the unnested array.
Consider this sample data:
contact_id | study_id
---------: | -------:
1 | 1
2 | 2
5 | 5
Now if we run the following query:
select cvs.contact_id, cvs.study_id, old_ids.old_ids
from contacts_vs_studies as cvs
left join unnest('{1,2,3,4,68}'::int[]) as old_ids
on cvs.contact_id = old_ids.old_ids
We get:
contact_id | study_id | old_ids
---------: | -------: | ------:
1 | 1 | 1
2 | 2 | 2
5 | 5 | null
Now you can see that the third record was not matched. To filter only on matching records, you can use an inner join instead of a left join.
select cvs.contact_id, cvs.study_id
from contacts_vs_studies as cvs
inner join unnest('{1,2,3,4,68}'::int[]) as old_ids
on cvs.contact_id = old_ids.old_ids
contact_id | study_id
---------: | -------:
1 | 1
2 | 2
Alternatively, if you want only non-matching records:
select cvs.contact_id, cvs.study_id
from contacts_vs_studies as cvs
left join unnest('{1,2,3,4,68}'::int[]) as old_ids
on cvs.contact_id = old_ids.old_ids
where old_ids.old_ids is null
contact_id | study_id
---------: | -------:
5 | 5

I need to join the current table of contacts with an unnested array of ids and delete the record if the unnested integer matches contact_id.
Sounds like you don't need a JOIN at all. Just write the DELETE command directly:
DELETE FROM study.contacts_vs_studies AS cvs
WHERE cvs.contact_id = ANY('{1,2,3,4,68}'::INT[])
AND cvs.study_id = 6

Related

Jsonb_object_keys() does not return any rows in left join if the right side table does not have any matching records

This is db query .
select users.Id,jsonb_object_keys(orders.metadata::jsonb) from users left join orders on users.userId=orders.userId where users.userId=2;
users table orders table
------------------- -----------------------------------------------------
|userId| name | | userId|orderId|metadata |
| 1 | john | | 1 | 1 | {"orderName":"chess","quantity":1}|
| 2 | doe | | 1 | 2 | {"orderName":"cube" ,"quantity":1}|
------------------- -----------------------------------------------------
Why there are no rows returned by the query ?
Very Nice and tricky question. to achieve what you want you should try below query:
select
t1.userid,
t2.keys
from
users t1
left join (select userid, orderid, jsonb_object_keys(metadata) as keys from orders) t2
on t1.userid=t2.userid
Your Query seems correct but there is catch. When you are left joining both tables without jsonb_object_keys(metadata), it will work as you are expecting. But when you use with this function then this function will return a set of records for each rows of select statement and perform simple join with rest of the columns internally. That's why it will remove the rows having NULL value in second column.
You should left join to the result of the jsonb_each() call:
select users.userid, meta.*
from users
left join orders on users.userid = orders.userid
left join jsonb_object_keys(orders.metadata::jsonb) as meta on true
where users.userid = 2;

Fetch records with distinct value of one column while replacing another col's value when multiple records

I have 2 tables that I need to join based on distinct rid while replacing the column value with having different values in multiple rows. Better explained with an example set below.
CREATE TABLE usr (rid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(12) NOT NULL,
email VARCHAR(20) NOT NULL);
CREATE TABLE usr_loc
(rid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
code CHAR NOT NULL PRIMARY KEY,
loc_id INT NOT NULL PRIMARY KEY);
INSERT INTO usr VALUES
(1,'John','john#product'),
(2,'Linda','linda#product'),
(3,'Greg','greg#product'),
(4,'Kate','kate#product'),
(5,'Johny','johny#product'),
(6,'Mary','mary#test');
INSERT INTO usr_loc VALUES
(1,'A',4532),
(1,'I',4538),
(1,'I',4545),
(2,'I',3123),
(3,'A',4512),
(3,'A',4527),
(4,'I',4567),
(4,'A',4565),
(5,'I',4512),
(6,'I',4567);
(6,'I',4569);
Required Result Set
+-----+-------+------+-----------------+
| rid | name | Code | email |
+-----+-------+------+-----------------+
| 1 | John | B | 'john#product' |
| 2 | Linda | I | 'linda#product' |
| 3 | Greg | A | 'greg#product' |
| 4 | Kate | B | 'kate#product' |
| 5 | Johny | I | 'johny#product' |
| 6 | Mary | I | 'mary#test' |
+-----+-------+------+-----------------+
I have tried some queries to join and some to count but lost with the one which exactly satisfies the whole scenario.
The query I came up with is
SELECT distinct(a.rid)as rid, a.name, a.email, 'B' as code
FROM usr
JOIN usr_loc b ON a.rid=b.rid
WHERE a.rid IN (SELECT rid FROM usr_loc GROUP BY rid HAVING COUNT(*) > 1);`
You need to group by the users and count how many occurrences you have in usr_loc. If more than a single one, then replace the code by B. See below:
select
rid,
name,
case when cnt > 1 then 'B' else min_code end as code,
email
from (
select u.rid, u.name, u.email, min(l.code) as min_code, count(*) as cnt
from usr u
join usr_loc l on l.rid = u.rid
group by u.rid, u.name, u.email
) x;
Seems to me that you are using MySQL, rather than IBM DB2. Is that so?

PostgreSQL Group By not working as expected - wants too many inclusions

I have a simple postgresql table that I'm tying to query. Imaging a table like this...
| ID | Account_ID | Iteration |
|----|------------|-----------|
| 1 | 100 | 1 |
| 2 | 101 | 1 |
| 3 | 100 | 2 |
I need to get the ID column for each Account_ID where Iteration is at its maximum value. So, you'd think something like this would work
SELECT "ID", "Account_ID", MAX("Iteration")
FROM "Table_Name"
GROUP BY "Account_ID"
And I expect to get:
| ID | Account_ID | MAX(Iteration) |
|----|------------|----------------|
| 2 | 101 | 1 |
| 3 | 100 | 2 |
But when I do this, Postgres complains:
ERROR: column "ID" must appear in the GROUP BY clause or be used in an aggregate function
Which, when I do that it just destroys the grouping altogether and gives me the whole table!
Is the best way to approach this using the following?
SELECT DISTINCT ON ("Account_ID") "ID", "Account_ID", "Iteration"
FROM "Marketing_Sparks"
ORDER BY "Account_ID" ASC, "Iteration" DESC;
The GROUP BY statement aggregates rows with the same values in the columns included in the group by into a single row. Because this row isn't the same as the original row, you can't have a column that is not in the group by or in an aggregate function. To get what you want, you will probably have to select without the ID column, then join the result to the original table. I don't know PostgreSQL syntax, but I assume it would be something like the following.
SELECT Table_Name.ID, aggregate.Account_ID, aggregate.MIteration
(SELECT Account_ID, MAX(Iteration) AS MIteration
FROM Table_Name
GROUP BY Account_ID) aggregate
LEFT JOIN Table_Name ON aggregate.Account_ID = Table_Name.Account_ID AND
aggregate.MIteration = Tabel_Name.Iteration

How to use COUNT() in more that one column?

Let's say I have this 3 tables
Countries ProvOrStates MajorCities
-----+------------- -----+----------- -----+-------------
Id | CountryName Id | CId | Name Id | POSId | Name
-----+------------- -----+----------- -----+-------------
1 | USA 1 | 1 | NY 1 | 1 | NYC
How do you get something like
---------------------------------------------
CountryName | ProvinceOrState | MajorCities
| (Count) | (Count)
---------------------------------------------
USA | 50 | 200
---------------------------------------------
Canada | 10 | 57
So far, the way I see it:
Run the first SELECT COUNT (GROUP BY Countries.Id) on Countries JOIN ProvOrStates,
store the result in a table variable,
Run the second SELECT COUNT (GROUP BY Countries.Id) on ProvOrStates JOIN MajorCities,
Update the table variable based on the Countries.Id
Join the table variable with Countries table ON Countries.Id = Id of the table variable.
Is there a possibility to run just one query instead of multiple intermediary queries? I don't know if it's even feasible as I've tried with no luck.
Thanks for helping
Use sub query or derived tables and views
Basically If You You Have 3 Tables
select * from [TableOne] as T1
join
(
select T2.Column, T3.Column
from [TableTwo] as T2
join [TableThree] as T3
on T2.CondtionColumn = T3.CondtionColumn
) AS DerivedTable
on T1.DepName = DerivedTable.DepName
And when you are 100% percent sure it's working you can create a view that contains your three tables join and call it when ever you want
PS: in case of any identical column names or when you get this message
"The column 'ColumnName' was specified multiple times for 'Table'. "
You can use alias to solve this problem
This answer comes from #lotzInSpace.
SELECT ct.[CountryName], COUNT(DISTINCT p.[Id]), COUNT(DISTINCT c.[Id])
FROM dbo.[Countries] ct
LEFT JOIN dbo.[Provinces] p
ON ct.[Id] = p.[CountryId]
LEFT JOIN dbo.[Cities] c
ON p.[Id] = c.[ProvinceId]
GROUP BY ct.[CountryName]
It's working. I'm using LEFT JOIN instead of INNER JOIN because, if a country doesn't have provinces, or a province doesn't have cities, then that country or province doesn't display.
Thanks again #lotzInSpace.

Update Count column in Postgresql

I have a single table laid out as such:
id | name | count
1 | John |
2 | Jim |
3 | John |
4 | Tim |
I need to fill out the count column such that the result is the number of times the specific name shows up in the column name.
The result should be:
id | name | count
1 | John | 2
2 | Jim | 1
3 | John | 2
4 | Tim | 1
I can get the count of occurrences of unique names easily using:
SELECT COUNT(name)
FROM table
GROUP BY name
But that doesn't fit into an UPDATE statement due to it returning multiple rows.
I can also get it narrowed down to a single row by doing this:
SELECT COUNT(name)
FROM table
WHERE name = 'John'
GROUP BY name
But that doesn't allow me to fill out the entire column, just the 'John' rows.
you can do that with a common table expression:
with counted as (
select name, count(*) as name_count
from the_table
group by name
)
update the_table
set "count" = c.name_count
from counted c
where c.name = the_table.name;
Another (slower) option would be to use a co-related sub-query:
update the_table
set "count" = (select count(*)
from the_table t2
where t2.name = the_table.name);
But in general it is a bad idea to store values that can easily be calculated on the fly:
select id,
name,
count(*) over (partition by name) as name_count
from the_table;
Another method : Using a derived table
UPDATE tb
SET count = t.count
FROM (
SELECT count(NAME)
,NAME
FROM tb
GROUP BY 2
) t
WHERE t.NAME = tb.NAME