Skip all rows if one row id is present in another table - sql

I have three tables with this relation:
T1.journo = T2.journo
T2.recid = T3.spid
T1
ticketno journo
1 A1
2 A2
T2
journo recid
A1 1
A1 2
A1 3
A2 4
A2 5
A2 6
T3
spid
2
I want only those entries of T1 where T2.recid is not present in T3.spid.
Below query will just omit the 2nd row of T2. But I want all the rows of T2 with journo=A1 omitted because one of the recid of A1 is present in T3.
select T1.* from T1 join T2 on T1.journo = T2.journo
where T2.recid not in (select spid from T3)
Desired Output:
ticketno journo
2 A2
Any tips?

To me, this sounds like not exists:
select t1.*
from t1
where not exists (select 1
from t2 join
t3
on t2.recid = t3.spid
where t2.journo = t1.journo
);

Should be able to use not in with a sub query.
declare #t1 table (ticketno int identity(1,1), journo varchar(2))
declare #t2 table (journo varchar(2), recid int identity(1,1))
declare #t3 table (spid int)
insert into #t1
values
('A1'),
('A2')
insert into #t2
values
('A1'),
('A1'),
('A1'),
('A2'),
('A2'),
('A2')
insert into #t3
values
(2)
select T1.* , T2.*
from #t1 T1
inner join #t2 T2 on T1.journo = T2.journo
where T2.journo not in (select t22.journo from #t2 t22 where t22.recid in (select * from #t3))
Or, not exists correlated
where not exists(select t22.journo from #t2 t22 where t22.recid in (select * from #t3) and t22.journo = T2.journo)

Join T2 on T3 with left a left join. group on the t1 variables then do a having check of COUNT(t3.spid) = 0.
Any nulls shouldn't be counted and so zero on the count is what your looking for.
SELECT t1.*
FROM #t1 t1
JOIN #t2 t2 ON t2.journo = t1.journo
LEFT JOIN #t3 t3 ON t3.spid = t2.recid
GROUP BY t1.ticketno, t1.journo
HAVING COUNT(t3.spid) = 0

SQL, the ugly but necessary part of our lives:
SELECT * FROM T1
INNER JOIN
(SELECT a1.journo
, SUM(spid_present) AS 'total_spids'
FROM
(SELECT T2.journo
, T2.ticketno
, CASE
WHEN t3.spid IS NOT NULL
THEN 1
ELSE 0
END AS 'spid_present'
FROM T2
LEFT JOIN T3
ON T2.recid = T3.spid) a1
GROUP BY a1.journo) a2
ON T1.journo = a2.journo
AND a2.total_spids = 0}

Using EXCEPT:
create table t1 (ticketno int, journo char(2))
insert into t1 values (1, 'A1'), (2, 'A2')
create table t2 (journo char(2), recid int)
insert into t2 values ('A1', 1), ('A1', 2), ('A1', 3), ('A2', 4), ('A2', 5), ('A2', 6)
create table t3 ([SPID] int)
insert into t3 values (2)
select t1.* from t1
except
select t1.*
from t1
inner join t2 on t2.journo = t1.journo
inner join t3 on t2.recid = t3.[SPID]

You can try the below SQL query.
select T.* from T1 , T2 a where T1.journo = a.journo and a.recid not exists (select 1 from T3, T2 where T2.recid= T3.spid and T2.recid =a.reci);

By my understanding, simple join
select t1.* from #t1 t1
join #t2 t2
on t1.journo <> t2.journo
join #t3 t3
on t2.recid = t3.spid

Related

How to update a value from other tables in postgres

currently I have 3 tables
Table 1:
tb1_id
values
1
4
Table 2:
tb1_id
tbl3_id
1
5
Table 3:
tb3_id
values
5
2
For some reason the values from table 1 are not the same in table 3 (as shown above), I need to update the values of the table 1 with the table 3, but I am not able to do so, so far this is my query:
UPDATE
table1 t1
SET
values = temp_tbl.values
FROM
(
SELECT t2.tb1_id, t3.values FROM table2 t2
JOIN
table3 t3 ON t2.tbl3_id = t3.tbl3_id
) temp_tbl
WHERE
t1.tbl1_id = temp_tbl.tbl1_id
AND
t1.values != temp_tbl.values;
OK so if I understood you correctly, you just need to make a slight correction to your code:
UPDATE
table1 t1
SET
values = temp_tbl.values
FROM
(
SELECT t2.tbl1_id, t3.values
FROM table2 t2
Inner JOIN table3 t3 ON t2.tbl3_id = t3.tbl3_id
) temp_tbl
WHERE
t1.tbl1_id = temp_tbl.tbl1_id
AND
t1.values != temp_tbl.values;
Here is DBFiddle link.
EDIT: This would also do:
with newData as (
select t1.tbl1_id, t3.Values
from Table1 t1
inner join Table2 t2 on t1.Tbl1_Id = t2.Tbl1_Id
inner join Table3 t3 on t2.Tbl3_Id = t3.Tbl3_Id
where t1.Values != t3.Values
)
UPDATE
table1
SET
values = newData.Values
FROM newData
where table1.tbl1_id = newData.tbl1_id;
this is how you can do it:
-- temporary tables
SELECT 1 TBL1_ID, 4 valuet1 INTO #t1
SELECT 1 TBL1_ID, 5 TBL3_Id INTO #t2
SELECT 5 TBL3_ID, 2 valuet3 INTO #t3
-- select with joins
select T1.*,T2.*, T3.*
from #t1 T1
INNER JOIN #t2 T2
ON T1.TBL1_ID = T2.TBL1_ID
INNER JOIN #t3 T3
ON T3.TBL3_ID = T2.TBL3_ID
-- update would be like this
update #t1 set valuet1 = T3.valuet3
from #t1 T1
INNER JOIN #t2 T2
ON T1.TBL1_ID = T2.TBL1_ID
INNER JOIN #t3 T3
ON T3.TBL3_ID = T2.TBL3_ID

T-SQL Group Check

I have three tables - Table_1, Table_2 and Table_3. Here's what each looks like:
Table_1
ID | TicketID | Rule
---------------------------
1 | 101 | NULL
Table_2
TicketID | Location
---------------------------
101 | A
101 | B
Table_3
Location | Rule
--------------------
A | R1
B | R1
A | R2
B | R2
C | R2
My goal is to populate the Rule column in Table_1 (which should be a select distinct of the Rule column in Table_3). The process should be:
Take the TicketID from Table_1. Join Table_1 to Table_2 on TicketID and get the Locations associated with that ticket.
Look up Table_3 and check what Rule needs to be applied. The check needs to be at the group level. For instance, in this example, Ticket 101 has locations A,B. Table_3 has A,B against Rule R1 but also has A,B,C against Rule R2. The correct rule should be R1 as Ticket 101 has no allocation for location C. Hope this make sense. What would be the easiest way of achieving this? Thanks in advance!
Please try the below code. Its working fine in SQL server 2012.
DECLARE #table_1 TABLE
(ID int, TicketID int, [Rule] Varchar(10))
DECLARE #table_2 TABLE
(TicketID int, Location Varchar(10))
DECLARE #table_3 TABLE
(Location Varchar(10),[Rule] Varchar(10))
INSERT #table_1
(ID,TicketID,[Rule])
VALUES
(1,101,NULL)
INSERT #table_2
(TicketID,Location)
VALUES
(101,'A'),
(101,'B')
INSERT #table_3
(Location,[Rule])
VALUES
('A','R1'),
('B','R1'),
('A','R2'),
('B','R2'),
('C','R2')
SELECT DISTINCT [RULE] FROM #table_3 t3 WHERE t3.Location IN
(SELECT t2.Location FROM #table_2 t2 INNER JOIN #table_1 t1 ON t1.TicketID = t2.TicketID)
AND [RULE] NOT IN
(SELECT t.[RULE] FROM #table_3 t WHERE t.Location NOT IN
(SELECT t2.Location FROM #table_2 t2 INNER JOIN #table_1 t1 ON t1.TicketID = t2.TicketID))
TRY THIS..
SELECT t1.TicketID
,t2.[Rule]
FROM (SELECT DISTINCT t2.TicketID,[Rule]
FROM #table_3 t3 INNER JOIN
#table_2 t2 ON t2.Location = t3.Location) t2
INNER JOIN #table_1 t1 ON t2.TicketID = t1.TicketID
Use this select statement with the above declared table variable
--http://stackoverflow.com/questions/37344006/t-sql-group-check
set nocount on
drop table #temp
DECLARE #table_1 TABLE
(ID int, TicketID int, [Rule] Varchar(10))
DECLARE #table_2 TABLE
(TicketID int, Location Varchar(10),obs int)
DECLARE #table_3 TABLE
(Location Varchar(10),[Rule] Varchar(10), obs int)
INSERT #table_1
(ID,TicketID,[Rule])
VALUES
(1,101,NULL)
INSERT #table_2
(TicketID,Location)
VALUES
(101,'A'),
(101,'B'),
--(101,'C')
(102,'A'),
(102,'B'),
(102,'C'),
(102,'S')
INSERT #table_3
(Location,[Rule])
VALUES
('A','R1'),
('B','R1'),
('A','R2'),
('B','R2'),
('C','R2'),
('S','R2')
declare #table_4 table (src varchar(2), id varchar(3), obs int)
insert into #table_4
select 't2', [ticketid], count(*) from #table_2 group by [ticketid]
Insert into #table_4
select 't3',[rule], count(*) from #table_3 group by [rule]
update #table_2
set obs = t4.obs
from #table_2 t2
join #table_4 t4 on t4.id = t2.ticketid
where t4.src = 't2'
update #table_3
set obs = t4.obs
from #table_3 t3
join #table_4 t4 on rtrim(t4.id) = rtrim(t3.[rule])
where t4.src = 't3'
select t2.ticketid,t2.location as t2location,t2.obs as t2obs,t3.location as t3location,t3.[Rule],t3.obs as t3obs
into #temp
from #table_2 t2
full join #table_3 t3 on t3.location = t2.location
delete #temp
where [rule] in (select [rule] from #temp where ticketid is null)
--select t.*
--from #temp t
select distinct ticketid,[rule]
from #temp
where t2location = t3location and t2obs = t3obs
Try this code :
SELECT DISTINCT [RULE] FROM Table_3 t3 WHERE t3.Location IN
(SELECT t2.Location FROM Table_2 t2 INNER JOIN Table_1 t1 ON t1.TicketID = t2.TicketID)
AND [RULE] NOT IN
(SELECT t.[RULE] FROM Table_3 t WHERE t.Location NOT IN
(SELECT t2.Location FROM Table_2 t2 INNER JOIN Table_1 t1 ON t1.TicketID = t2.TicketID))

SQL Join query to show records if exists in master table or not

Table1
id name color
1,'a','red'
2,'a','blue'
3,'b','red'
4,'c','red'
5,'d','red'
6,'a','green'
declare #t1 table (id int, name varchar(10),color varchar(5))
insert into #t1 values(1,'a','red')
insert into #t1 values(2,'a','blue')
insert into #t1 values(3,'b','red')
insert into #t1 values(4,'c','red')
insert into #t1 values(5,'d','red')
table t2 (master table )
color
red
blue
green
declare #t2 table (color varchar(5))
insert into #t2 values ('red')
insert into #t2 values ('blue')
insert into #t2 values ('green')
The output will be
'a','red'
'a','blue'
'a','green'
We need to retrieve the name from table 1 what are all having all the t2 color...
You can get the names in t1 that match all master colors using group by, having, and join:
select t1.name
from t1 join
t2
on t1.color = t2.color
group by t1.name
having count(distinct t1.color) = (select count(*) from t2);
This returns the names. If you want the detailed rows, then use this as a subquery or CTE and join t1 back to these results.
And to get the detailed rows:
with n as (
select t1.name
from t1 join
t2
on t1.color = t2.color
group by t1.name
having count(distinct t1.color) = (select count(*) from t2)
)
select t1.*
from t1 join
n
on t1.name = n.name;

left outer join in t-sql

I have the following two tables. I am using SQL Server 2008 R2
Create table #tmp1 (
a char(1)
)
Create table #tmp2 (
id int,
a char(1),
val int
)
insert #tmp1 values ('A')
insert #tmp1 values ('B')
insert #tmp1 values ('C')
insert #tmp2 values (1, 'A', 10)
insert #tmp2 values (1, 'B', 20)
insert #tmp2 values (2, 'A', 30)
insert #tmp2 values (2, 'C', 40)
select * from #tmp1 t1 left outer join #tmp2 t2 on t1.a = t2.a
order by t2.id
This returns the result set
A 1 A 10
B 1 B 20
C 2 C 40
A 2 A 30
I would like to have the following result set
A 1 A 10
B 1 B 20
C 1 null null
A 2 A 30
B 2 null null
C 2 C 40
Right now i am acheiving this by creating a new table with a cross join like this and then doing a outer join
select * into #tmp3 from #tmp1 cross join (select distinct ID from #tmp2) t
select * from #tmp3 t1 left outer join #tmp2 t2 on t1.a = t2.a and t1.id = t2.id
Is there a better way to do this ?
Thanks
To get what you want, you need a "driving" table. That is, you want a complete list of all combinations, and then to join to the other tables to get the matches. Here is one way:
select t1.a, t2.*
from (select t1.a as a, t2.id as id
from (select distinct a from #tmp1 t1) t1
cross join
(select distinct id from #tmp2 t2) t2
) driving left outer join
#tmp1 t1
on t1.a = driving.a left outer join
#tmp2 t2
on t2.id = driving.id and
t2.a = driving.a
order by t2.id
What you are looking for is a cartesian product of the values in #tbl1 and the values in the id column in #tbl2. Because the values in #tbl2.id are not unique it might be a better design to have an additional table with a row for each #tbl2.id value. Then you can use this solution:
Create table #tmp1 (
a char(1)
)
Create table #tmp2 (
id int,
a char(1),
val int
)
Create table #tmp3 (
id int
)
insert #tmp1 values ('A')
insert #tmp1 values ('B')
insert #tmp1 values ('C')
insert #tmp3 values (1)
insert #tmp3 values (2)
insert #tmp2 values (1, 'A', 10)
insert #tmp2 values (1, 'B', 20)
insert #tmp2 values (2, 'A', 30)
insert #tmp2 values (2, 'C', 40)
SELECT t3.id,t1.a,t2.val
FROM #tmp1 AS t1
CROSS JOIN #tmp3 AS t3
LEFT OUTER JOIN #tmp2 AS t2
ON t1.a = t2.a AND t3.id = t2.id
ORDER BY t3.id, t1.a;
If that is not an option use this instead:
SELECT t3.id,t1.a,t2.val
FROM #tmp1 AS t1
CROSS JOIN (SELECT DISTINCT id FROM #tmp2) AS t3
LEFT OUTER JOIN #tmp2 AS t2
ON t1.a = t2.a AND t3.id = t2.id
ORDER BY t3.id, t1.a;

How to combine GROUP BY and ROW_NUMBER?

I hope following sample code is self-explanatory:
declare #t1 table (ID int,Price money, Name varchar(10))
declare #t2 table (ID int,Orders int, Name varchar(10))
declare #relation table (t1ID int,t2ID int)
insert into #t1 values(1, 200, 'AAA');
insert into #t1 values(2, 150, 'BBB');
insert into #t1 values(3, 100, 'CCC');
insert into #t2 values(1,25,'aaa');
insert into #t2 values(2,35,'bbb');
insert into #relation values(1,1);
insert into #relation values(2,1);
insert into #relation values(3,2);
select T2.ID AS T2ID
,T2.Name as T2Name
,T2.Orders
,T1.ID AS T1ID
,T1.Name As T1Name
,T1Sum.Price
FROM #t2 T2
INNER JOIN (
SELECT Rel.t2ID
,MAX(Rel.t1ID)AS t1ID
-- the MAX returns an arbitrary ID, what i need is:
-- ,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
,SUM(Price)AS Price
FROM #t1 T1
INNER JOIN #relation Rel ON Rel.t1ID=T1.ID
GROUP BY Rel.t2ID
)AS T1Sum ON T1Sum.t2ID = T2.ID
INNER JOIN #t1 T1 ON T1Sum.t1ID=T1.ID
Result:
T2ID T2Name Orders T1ID T1Name Price
1 aaa 25 2 BBB 350,00
2 bbb 35 3 CCC 100,00
What i need is commented above, a way to get the ROW_NUMBER but also to Group By in the first place. So i need the sum of all T1-prices grouped by T2.ID in the relation-table and in the outer query the t1ID with the highest price.
In other words: How to change MAX(Rel.t1ID)AS t1ID to somewhat returning the ID with the highest price?
So the desired result is(notice that first T1ID changed from 2 to 1 since it has the higher price):
T2ID T2Name Orders T1ID T1Name Price
1 aaa 25 1 AAA 350,00
2 bbb 35 3 CCC 100,00
Note: in case you're wondering why i don't multiply Orders with Price: they are not realated(so i should have left off this column since it's a bit ambiguous, please ignore it, i've just added it to make all less abstract). Actually Orders must remain unchanged, that's the reason for the sub-query approach to join both and the reason why i need to group by in the first place.
Conclusion: obviously the core of my question can be answered by the OVER clause that can be applied to any aggregate function like SUM(see Damien's answer) what was new to me. Thank you all for your working approaches.
Wow, the other answers look complex - so I'm hoping I've not missed something obvious.
You can use OVER/PARTITION BY against aggregates, and they'll then do grouping/aggregating without a GROUP BY clause. So I just modified your query to:
select T2.ID AS T2ID
,T2.Name as T2Name
,T2.Orders
,T1.ID AS T1ID
,T1.Name As T1Name
,T1Sum.Price
FROM #t2 T2
INNER JOIN (
SELECT Rel.t2ID
,Rel.t1ID
-- ,MAX(Rel.t1ID)AS t1ID
-- the MAX returns an arbitrary ID, what i need is:
,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
,SUM(Price)OVER(PARTITION BY Rel.t2ID) AS Price
FROM #t1 T1
INNER JOIN #relation Rel ON Rel.t1ID=T1.ID
-- GROUP BY Rel.t2ID
)AS T1Sum ON T1Sum.t2ID = T2.ID
INNER JOIN #t1 T1 ON T1Sum.t1ID=T1.ID
where t1Sum.PriceList = 1
Which gives the requested result set.
Undoubtly this can be simplified but the results match your expectations.
The gist of this is to
Calculate the maximum price in a seperate CTE for each t2ID
Calculate the total price in a seperate CTE for each t2ID
Combine the results of both CTE's
SQL Statement
;WITH MaxPrice AS (
SELECT t2ID
, t1ID
FROM (
SELECT t2.ID AS t2ID
, t1.ID AS t1ID
, rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t1.Price DESC)
FROM #t1 t1
INNER JOIN #relation r ON r.t1ID = t1.ID
INNER JOIN #t2 t2 ON t2.ID = r.t2ID
) maxt1
WHERE maxt1.rn = 1
)
, SumPrice AS (
SELECT t2ID = t2.ID
, Price = SUM(Price)
FROM #t1 t1
INNER JOIN #relation r ON r.t1ID = t1.ID
INNER JOIN #t2 t2 ON t2.ID = r.t2ID
GROUP BY
t2.ID
)
SELECT t2.ID
, t2.Name
, t2.Orders
, mp.t1ID
, t1.ID
, t1.Name
, sp.Price
FROM #t2 t2
INNER JOIN MaxPrice mp ON mp.t2ID = t2.ID
INNER JOIN SumPrice sp ON sp.t2ID = t2.ID
INNER JOIN #t1 t1 ON t1.ID = mp.t1ID
;with C as
(
select Rel.t2ID,
Rel.t1ID,
t1.Price,
row_number() over(partition by Rel.t2ID order by t1.Price desc) as rn
from #t1 as T1
inner join #relation as Rel
on T1.ID = Rel.t1ID
)
select T2.ID as T2ID,
T2.Name as T2Name,
T2.Orders,
T1.ID as T1ID,
T1.Name as T1Name,
T1Sum.Price
from #t2 as T2
inner join (
select C1.t2ID,
sum(C1.Price) as Price,
C2.t1ID
from C as C1
inner join C as C2
on C1.t2ID = C2.t2ID and
C2.rn = 1
group by C1.t2ID, C2.t1ID
) as T1Sum
on T2.ID = T1Sum.t2ID
inner join #t1 as T1
on T1.ID = T1Sum.t1ID
The deduplication (to select the max T1) and the aggregation need to be done as distinct steps. I've used a CTE since I think this makes it clearer:
;WITH sumCTE
AS
(
SELECT Rel.t2ID, SUM(Price) price
FROM #t1 AS T1
JOIN #relation AS Rel
ON Rel.t1ID=T1.ID
GROUP
BY Rel.t2ID
)
,maxCTE
AS
(
SELECT Rel.t2ID, Rel.t1ID,
ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
FROM #t1 AS T1
JOIN #relation AS Rel
ON Rel.t1ID=T1.ID
)
SELECT T2.ID AS T2ID
,T2.Name as T2Name
,T2.Orders
,T1.ID AS T1ID
,T1.Name As T1Name
,sumT1.Price
FROM #t2 AS T2
JOIN sumCTE AS sumT1
ON sumT1.t2ID = t2.ID
JOIN maxCTE AS maxT1
ON maxT1.t2ID = t2.ID
JOIN #t1 AS T1
ON T1.ID = maxT1.t1ID
WHERE maxT1.PriceList = 1