NOT IN Clause for Multiple columns in SQL - mysql

I have a SQL query :
select * from product_structure_resources
where resource_id NOT IN (select resource_id from resources);
Now I need to add the columns like branch_id and scheme_id right after the where clause .
Like :
select * from product_structure_resources
where resource_id,scheme_id,branch_id NOT IN (select resource_id from resources);
How can I do it ?
P.S : The resources table does not have scheme_id and branch_id column in it. It has only resource_id column .

select *
from product_structure_resources pr
where not exists (select 1 from resources r where pr.resource_id = r.resource_id)
and not exists (select 1 from resources r where pr.scheme_id= r.resource_id)
and not exists (select 1 from resources r where pr.branch_id= r.resource_id)

This should return the exact same result as juergen d's query.
SELECT psr.*
FROM product_structure_resources AS psr
LEFT JOIN resources AS r ON r.resource_id = psr.resource_id)
LEFT JOIN resources AS s ON s.resource_id = psr.scheme_id)
LEFT JOIN resources AS b ON b.resource_id = psr.branch_id)
WHERE r.resource_id IS NULL
AND s.resource_id IS NULL
AND b.resource_id IS NULL
And just for fun, there also is this extreme (obscure and ill-advised) way:
SELECT *
FROM product_structure_resources AS psr
WHERE resource_id IN (
SELECT resources.resource_id FROM resources
WHERE resources.resource_id NOT IN (psr.resource_id, psr.scheme_id, psr.branch_id)
)

I think it may works and helps you
select *
from product_structure_resources
where concat(resource_id,scheme_id,branch_id) NOT IN
(select concat(resource_id,scheme_id,branch_id) from resources);

Related

How to join the result of a request to another table (to add column)

Edit
I have two tables : etape and etapex.
I need to find the maximum idProjet for each idEtape (in table etapex), and there link the idEtape and idProjet to 'nomEtapeandlivrable` from the table etape.
So I tried two different things :
SELECT etapexprojet.idEtape, idProjet, nometape, livrable
FROM etape, etapexprojet
WHERE etapexprojet.idetape = (SELECT MAX(etape.idetape) FROM etapexprojet )
Unfortunately, this is what I get :
Here's my other "solution":
The first step is to find the max value (I don't want to use group by) :
SELECT EX.idEtape
FROM etapexprojet EX
LEFT JOIN etapexprojet EX2
ON EX.idprojet = EX2.idprojet
AND EX.idetape < EX2.idetape
WHERE EX2.idetape IS NULL
But now I'm stuck, and I don't get how to join what I get from that first request to the table etape.
At the end, it should give me something like :
But with the columns nomEtape and livrable as well...
Thanks in advance for your help !
You can just JOIN the etape table to the results of your first query:
SELECT EX.idProjet, E.*
FROM etape E
JOIN etapexprojet EX ON EX.idEtape = E.idEtape
LEFT JOIN etapexprojet EX2
ON EX.idprojet = EX2.idprojet
AND EX.idetape < EX2.idetape
WHERE EX2.idetape IS NULL
ORDER BY idProjet
Demo on SQLFiddle
There are various solution to this greatest-n-per-group problem.
One simple method is to start from this aggregate query that gives you the maximum idEtape per IdProjet:
select idProjet, max(idEtape) idEtape from etapex group by idProjet
Then you can simply join this with the original table:
select e.*, p.idProjet
from etape e
inner join (
select idProjet, max(idEtape) idEtape from etapex group by idProjet
) p on p.idEtape = e.idEtape
Try this:
SELECT ET.idEtape, ETX.idProjet, ET.nomEtape, ET.livrable
FROM etape ET
LEFT JOIN etapexprojet ETX ON ET.idEtape = ETX.idEtape
WHERE ETX.idEtape = (SELECT MAX(idEtape) FROM etapexprojet WHERE idProjet = ETX.idProjet);

repeated rows in json_agg() in query with 2 lateral joins

I have a strange result when performing a lateral join on a query
I have the following table structure
task->id
comment -> id , taskId, comment
tasklink -> taskId, type, userid
with a single task record (id 10), 1 comment record ("row1", "a test comment") and 5 tasklink records (all with taskid 10)
I expected this query
select task.id,
json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments,
json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
FROM task
left join lateral (select c.* from comment c where task.id = c.taskid) c on true
left join lateral (select b.* from taskuserlink b where task.id = b.taskid) b on true
where task.id = 10
GROUP BY task.id ;
to return
id | comments | users
---------------------------------------------------------------------
10 "[{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},"type":"closedBy","user":5},"type":"updatedBy","user":5},"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
instead, I got this
id | comments | users
10 "[{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},{"type":"closedBy","user":5},{"type":"updatedBy","user":5},{"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
ie , for every link row, the comment row is duplicated
I am thinking that I am missing something really obvious, but as I have only just started using Postgres (and sql ) I'm a little stumped
I would appreciate some guidance on where I'm going wrong
Move the aggregates into subqueries:
select id, comments, users
from task t
left join lateral (
select json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments
from comment c
where t.id = c.taskid
) c on true
left join lateral (
select json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
from taskuserlink b
where t.id = b.taskid
) b on true
DbFiddle.

MySQL combine UPDATE and SELECT query

I have the following SELECT statement that returns data, example below:
SELECT performers.ID,
performers.Name,
COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN performers
ON deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN ( 27952, 27951, 27950, 27949, 27948 )
GROUP BY Name;
Example data returned:
ID Name CountOfDeals CountOfAliases
27952 Christine Hoberg 1 0
27951 Indian Jewelry 1 0
27952 Kinky Friedman 5 3
27949 KJ-52 1 0
27960 River Whyless 1 0
I want to combine this with the following UPDATE statement
UPDATE performers
SET RawAliasCount = CountOfAliases,
RawDealCount = CountOfDeals
WHERE ID = ?
All the values needed to run the update statement are returned in the select statement above so hopefully this should be pretty easy.
Thanks.
Use update with join:
UPDATE performers p JOIN
(SELECT performers.ID, performers.Name, COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN
performers
on deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN (27952, 27951, 27950, 27949, 27948)
GROUP BY Name
) pp
ON pp.id = p.id
SET RawAliasCount = pp.CountOfAliases,
RawDealCount = pp.CountOfDeals;
UPDATE performers
SET performers.RawAliasCount = count_table.CountOfAliases, performers.RawDealCount = count_table.CountOfDeals
FROM performers
INNER JOIN
(
SELECT
performers.ID, performers.Name, COUNT(*) AS CountOfDeals,
COUNT(DISTINCT(deals.Name)) AS CountOfAliases
FROM deals RIGHT JOIN performers on deals.name LIKE CONCAT('%', performers.name, '%')
WHERE performers.ID IN (27952, 27951, 27950, 27949, 27948)
GROUP BY Name
) count_table
ON count_table.ID = performers.ID;
When this type of question is asked, thank you to put the tables schema.
edit : sorry, it's sql-server syntax.

MYSQL Select As Case when number of rows statement

I'm trying to find the number of notifications for each user, I am having a little problem, I had the query working perfect for what I needed it for, then I changed my table around just a little bit.
Working query:
$numNotifications = mysql_num_rows(mysql_query("
SELECT
N.*,
P.*
FROM
notifications N,
posts P
WHERE
N.userID='$session'
AND
(
N.action='1' OR N.action='2'
)
AND
N.uniqueID=P.id AND P.state='0'"
));
However, uniqueID is now different for some rows. when N.aciton is "1" then N.uniqueID should be compared to P.id, however when N.action is "2" it should compare a row in that table with P.id.
Example query, (that SHOULD work, but doesn't)
$numNotifications = mysql_num_rows(mysql_query("
SELECT
N.*,
P.*,
C.*,
(CASE WHEN (
N.action = 2 AND N.state = 0
)
THEN
C.postID ELSE N.uniqueID END
) AS postId
FROM
notifications N,
posts P,
comments C
WHERE
N.userID='$session'
AND
(
N.action='1' OR N.action='2'
)
AND
postId=P.id AND P.state='0'"
));
diagram of my 3 table structures:
http://i41.tinypic.com/nyzolg.png
here you go :)
SELECT
COUNT(`notif_id`) AS `number_of_new_notifications`
FROM
(
SELECT
`notifications`.`id` AS `notif_id`
FROM
`notifications`
JOIN
`posts`
ON
`notifications`.`uniqueID`=`posts`.`id`
WHERE
`notifications`.`userID`='$session'
AND
`notifications`.`action`=1
AND
`notifications`.`state`=0
AND
`posts`.`state`=0
UNION ALL
SELECT
`notifications`.`id` AS `notif_id`
FROM
`notifications`
JOIN
`comments`
ON
`notifications`.`uniqueID`=`comments`.`id`
JOIN
`posts`
ON
`comments`.`postID`=`posts`.`id`
WHERE
`notifications`.`userID`='$session'
AND
`notifications`.`action`=2
AND
`notifications`.`state`=0
AND
`comments`.`state`=0
AND
`posts`.`state`=0
) AS notification_ids;

selecting where in multiple join tables

I have the logic worked out, just not sure how to best write this query.
the logic is
we have a deal ID of 1
a deal is linked to multiple regions
a deal is linked to multiple interests
a user is linked to multiple regions
a user is linked to multiple interests
we want all users where....
the user is linked to the same region as a deal
userRegionLink url, dealRegionLink drl
url.regionId is in drl.regionId where drl.dealId = 1
the user is linked to the same interest as a deal
userInterestLink uil, dealInterestLink dil
uil.interestId is in dil.interestId where dil.dealId = 1
this would give us a list of the users
now we need to select distinct from the list so we only end up sending each user a single email
But I have no idea what the best way to write this query would be.
We are dealing with a few tables here
We have
users which has all the user Information in it userId and other columns not important
userInterestLink which has userId and interestId
dealInterestLink which has dealId and interestId
userRegionLink which has userId and regionId
dealRegionLink which has dealId and regionId
so what we are wanting in the end is all the user info which matches.
I take RC's answer and modify it
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil ON (uil.userId = u.userId)
JOIN userRegionLink url ON (url.userId = u.userId)
WHERE interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
as there is no need for LEFT JOIN if I exclude the NULL rows afterwards.
A more "symmetric" version without subqueries and with USING would be
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil USING (userId)
JOIN userRegionLink url USING (userId)
JOIN dealInterestLink dil USING (interestId)
JOIN dealRegionLink drl USING (regionId, dealId)
WHERE dealId = 1
Untested as well.
Something like:
SELECT u.userId, uil.interestId, url.regionId FROM users u
LEFT JOIN userInterestLink uil ON (uil.userId = u.userId)
LEFT JOIN userRegionLink url ON (url.userId = u.userId)
WHERE uil.interestId IS NOT NULL AND uil.interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND url.regionId IS NOT NULL AND url.regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
? If result is OK, you can then SELECT DISTINCT u.userId FROM users u -- ...
(not tested)
SELECT `u`.*
FROM `users` AS `u`
JOIN `userRegionLink` `userReg` USING ( `userId` )
JOIN `userInterestLink` `userInt` USING ( `userId` )
JOIN `dealInterestLink` `dealInt` USING ( `interestId` )
JOIN `dealRegionLink` `dealReg` USING ( `regionId` )
JOIN `deal` `d` ON ( `dealInt`.`dealId` && `dealReg`.`dealId` && `d`.`dealId` = 1 )
GROUP BY `u`.`userId`
Tested locally using dummy data and presumed schema. Worked OK.