TSQL - excluding the rows that matches with temp table - sql

I need a help with SQL SELECT query.
I have [Details] sql table below. I want to select rows that have matching group id and that does not exist in given temp table
DetailID GroupID TemplateID DocumentID
------------------------------------------------------
1 A 2 NULL
2 A NULL 33
3 A 10 NULL *
4 B NULL 33
5 B 4 NULL *
6 C 2 NULL
7 C 4 NULL *
8 C NULL 55 *
#tmpDetails - A Templarary table that has TemplateID and DocumentID
TemplateID DocumentID
---------------------------
2 NULL
NULL 33
I want to select rows that are in group A, B and C and does not have matching TemplateID and DocumentID from #tmpDetail table
so the select query should return the rows with detail ID 3,5,7,8
The query below does not return anything
SELECT * from Details D
WHERE D.GroupID IN ('A','B','C') AND NOT EXISTS
(SELECT d2.DetailID FROM Details d2
JOIN #tmpDetails t ON d2.TemplateID IS NOT NULL
AND d2.TemplateID = t.TemplateID
OR d2.DocumentID IS NOT NULL AND d2.DocumentID = t.DocumentID)
whatever the condition have in JOIN it should be the where condition but i am sure how do compose that SQL

You did not connect your nested query with the main table (D).
You can get rid of the join and do this:
SELECT * FROM Details D
WHERE D.GroupID IN ('A','B','C') AND NOT EXISTS
(SELECT 1 FROM #tmpDetails t
WHERE (D.TemplateID IS NOT NULL AND D.TemplateID = t.TemplateID) OR
(D.DocumentID IS NOT NULL AND D.DocumentID = t.DocumentID))

SELECT D.*
FROM
Details D
OUTER APPLY
(
SELECT TOP 1 1 Match
FROM #tmpDetails T
WHERE
D.TemplateID = T.TemplateID
OR D.DocumentID = T.DocumentID
) T
WHERE
D.GroupID IN ('A','B','C')
AND T.Match IS NULL

You could use NOT EXISTS and subquery with INTERSECT to handle NULL values:
SELECT * FROM Details D
WHERE D.GroupID IN ('A', 'B', 'C')
AND NOT EXISTS (SELECT TemplateID, DocumentID FROM #tmpDetails INTERSECT
SELECT D.TemplateID, D.DocumentID)

Related

Select groups of users if one or more user made a comment

Consider the following table1
user group comment
----------------------
1 a foo
2 a
3 a
4 b bar
5 c
6 c
7 d
8 d
9 d
10 d
11 e bax
12 e baz
I need to make 2 queries, each of which returns groups:
query 1: groups of which at least 1 user has made a comment
query 2: groups of which no user has made a comment
result of query 1:
user group comment
----------------------
1 a foo
2 a
3 a
4 b bar
11 e bax
12 e baz
result of query 2:
user group comment
----------------------
5 c
6 c
7 d
8 d
9 d
I tried the following but then I saw some of the same users in both groups:
select *
from [table1] t1
where t1.[group] in (
select distinct [group] from [table] where [comment] <> ''
)
order by t1.[user] asc
select *
from [table1] t1
where t1.[group] in (
select distinct [group] from [table] where [comment] = ''
)
order by t1.[user] asc
I then realised this is because in the same group, comment can be either set (comment <> '') AND not set (comment = '') but I don't know how to solve this in my queries.
Any help would be greatly appreciated.
You can use exists and not exists:
This gives you the users of groups in which comments have been posted.
select t.*
from mytable t
where exists (
select 1 from mytable t1 where t1.group = t.group and t1.comment is not null
)
To get the users of groups without any comment, you can just turn exists to not exists.
You can use exists and not exists. For groups with a comment:
select t1.*
from table1 t1
where exists (select 1
from table1 tt1
where tt1.group = t1.group and
tt1.comment is not null
);
Use not exists for the other set of rows.

Select rows where all values within group are NULL

How to:
Select rows where all rows within group are NULL. Below group is defined by id. I
wish to select rows where all values are NULL.
Id Var1
1 NULL
1 NULL
1 NULL
2 10
2 20
2 30
3 NULL
3 30
What I have tried:
select id
from table
where all var1 is null
group by id
Desired result:
id var1
1 NULL
1 NULL
1 NULL
Use having instead of where. It filters after aggregation:
select id
from table
group by id
having max(var1) is null;
A similar method uses:
having count(var1) = 0
You can try with this query:
select id,Var1
from table1 as a
where not exists (select id
from table1 as b
where a.id=b.id and b.Var1 is not null)
The subquery get the id that have values not null, so you don get them in the main query

SQL update all records except the last one with a value

I need to make a query where only the last line of each user that has a car gets a license plate number.
ID UserId LicensePlate HasCar
1 1 ABC123 1
2 1 ABC123 1
3 2 NULL 0
4 3 UVW789 1
5 3 UVW789 1
Should become:
ID UserId LicensePlate HasCar
1 1 NULL 1
2 1 ABC123 1
3 2 NULL 0
4 3 NULL 1
5 3 UVW789 1
So I basically need to find all users with a licenseplate and change all but the last one and make the LicensePlate NULL
Assuming the ID column is an identity column so it can provide the ordering, something like this should do the trick:
;WITH CTE AS
(
SELECT Id,
UserId,
LicensePlate,
ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY Id DESC) rn
FROM Table
WHERE HasCar = 1
)
UPDATE CTE
SET LicensePlate = NULL
WHERE rn > 1
You can try this
UPDATE l
SET l.LicensePlate = null
FROM Car l
INNER JOIN (SELECT UserId, Max(Id) AS max_id
FROM Car
GROUP BY UserId) m ON m.UserId = l.UserId
AND m.max_id <> l.id
You can do it with a join on the table itself like that :
UPDATE car c
INNER JOIN car c2 ON c.userId = c2.userId AND c.id < c2.id AND c.HasCar = 1 AND c2.HasCar = 1
SET c.LicensePlate = NULL
The condition c.id < c2.id will avoid to select the last line
By using LAG Function also you can achieve it.
;WITH License(ID,UserId,LicensePlate,HasCar)
as
(
SELECT 1,1,'ABC123',1 UNION ALL
SELECT 2,1,'ABC123',1 UNION ALL
SELECT 3,2,NULL ,0 UNION ALL
SELECT 4,3,'UVW789',1 UNION ALL
SELECT 5,3,'UVW789',1
)
SELECT ID,UserId,LAG(LicensePlate,1,NULL) OVER(PARTITION BY UserId ORDER BY LicensePlate),HasCar FROM License

Postgresql query with left join and having

Postgresql 9.1: I have a query that must return the values of a second table only if the aggregate function SUM of two columns is greater than zero.
This is the data:
Table a
id
---
1
2
3
Table b
id fk(table a)
---------------
1 1
2 null
3 3
Table c
id fk(table b) amount price
-----------------------------------
1 1 1 10 --positive
2 1 1 -10 --negative
3 3 2 5
As you can see, table b has some ids from table a, and table c can have 1 or more references to table b, table c is candidate to be retrieved only if the sum(amount * price ) > 0.
I wrote this query:
SELECT
a.id, b.id, SUM(c.amount * c.price) amount
FROM
tablea a
LEFT JOIN
tableb b ON b.fk = a.id
LEFT JOIN
tablec c ON c.fk = b.id
GROUP BY
a.id, b.id
HAVING
SUM(c.amount * c.price) > 0
But this query is not retrieving all rows from table a just the row 1 and I need the two rows. I understand this is happening because of the HAVING clause but I don't know how to rewrite it.
Expected result
a b sum
------------------
1 null null -- the sum of 1 * 10 (rows 1 and two) = 0 so its not retrieved.
2 null null -- no foreign key in second table
3 3 10 -- the sum of 2 * 5 (row 3) > 0 so it's ok.
Try this:
SELECT A.ID, B.ID, C.ResultSum
FROM TableA A
LEFT JOIN TableB B ON (B.FK = A.ID)
LEFT JOIN (
SELECT FK, SUM(Amount * Price) AS ResultSum
FROM TableC
GROUP BY FK
) C ON (C.FK = B.ID) AND (ResultSum > 0)
See demo here.

Random select preventing some duplicates

From Table
Name GroupID
a Null
b 1
c 1
d 2
e Null
f Null
g 3
Result expected from random top 4 selection
Name GruopID
a Null
b 1
e Null
g 3
Resuming I want to get random names but only 1 kind of groupid if groupid is <> null
The select Newid() type returns
"Select Top(4) * FROM Table Order By NEWID()"
Name GruopID
a Null
b 1
e Null
c 1
I donĀ“t want that. Hope i made my self clear!
Thanks in advance
You can try this (on SQL Server 2005+).
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY GroupId ORDER BY NEWID()) Corr
FROM YourTable
)
SELECT TOP 4 Name, GroupId
FROM CTE
WHERE GroupId IS NULL OR Corr = 1
ORDER BY NEWID()