MS SQL match table on multiple rows - sql-server

I want to join two tables based on multiple rows, and can't find the correct way to do this.
The tables looks something like this :
table1
Id Location LocationNo
1 1 1
1 2 2
1 3 3
2 1 1
2 2 2
3 2 1
3 1 2
3 3 3
4 1 1
4 2 2
4 3 3
4 4 4
table2
Location LocationNo
1 1
2 2
3 3
I want to get the Id from table1 that match exactly the rows in table2.
I expect that the return should be Id 1.
I've tried the query bellow but the result are not the one expected.
SELECT t.Id
FROM table1 t1
WHERE EXISTS (SELECT 1
FROM table2 t2
WHERE t1.LocationId = t2.LocationId
AND t1.LocationNo = t2.LocationNo)
Any suggestions? Thanks.
EDIT :
table2
Location LocationNo
1 1
2 2
3 3
5 4
For this case I expect that the result should be null

Make use of Inner Join.
select t1.Id -- or t2.id, depends which table you want the ID from
from table1 t1
inner join table2 t2
on t1.LocationId = t2.LocationId
and t1.LocationNo = t2.LocationNo

Select Distinct t1.Id --< Use Distinct to return unique values only
From table1 t1
Inner Join table2 t2 --< Use Inner Join instead of "where exists"
On t1.LocationId = t2.LocationId
AND t1.LocationNo = t2.LocationNo
Update: It actually turned out to be more interesting than that: here we are looking for the exact match on all rows:
Select t1.Id
From table1 t1
Left Join table2 t2 --< Left Join to register NULLs from table2
On t1.LocationId = t2.LocationId
AND t1.LocationNo = t2.LocationNo
Group By t1.Id
Having --< Number of records from each table equals the count of matching criteria rows
Count(t1.LocationId) = (Select Count(*) From table2)
AND Count(t2.LocationId) = (Select Count(*) From table2)
Source data for testing:
With table1 As (
Select * From (Values
(1, 1, 1),
(1, 2, 2),
(1, 3, 3),
(2, 1, 1),
(2, 2, 2),
(3, 2, 1),
(3, 1, 2),
(3, 3, 3),
(4, 1, 1),
(4, 2, 2),
(4, 3, 3),
(4, 4, 4)
) V (Id, LocationId, LocationNo)
), table2 As (
Select * From (Values
(1, 1),
(2, 2),
(3, 3)
) V (LocationId, LocationNo)
)

I think you are looking for this
IF EXISTS(SELECT 1
FROM (SELECT Count(1) cnt,
id
FROM Table1 a
GROUP BY id) a
JOIN (SELECT t1.Id,
Count(1) cnt
FROM table1 t1
JOIN table2 t2
ON t1.[Location] = t2.[Location]
AND t1.LocationNo = t2.LocationNo
GROUP BY t1.Id
HAVING Count(1) = (SELECT Count(1)
FROM table2)) b
ON a.cnt = b.cnt
AND a.Id = b.id)
WITH tot_count
AS (SELECT Count(1) cnt,
id
FROM #Table1 a
GROUP BY id),
sub_cnt
AS (SELECT t1.Id,
Count(1) cnt
FROM table1 t1
JOIN table2 t2
ON t1.[Location] = t2.[Location]
AND t1.LocationNo = t2.LocationNo
GROUP BY t1.Id
HAVING Count(1) = (SELECT Count(1)
FROM table2))
SELECT b.id
FROM tot_count a
JOIN sub_cnt b
ON a.cnt = b.cnt
AND a.Id = b.id
ELSE
SELECT NULL

Related

How to join two Tables which have no unique ID

I am trying to Left join Table 1 to table 2
Table1 Table2
ID Data ID Data2
1 r 1 q
2 t 1 a
3 z 2 x
1 u 3 c
After i have left joined this two Tables i would like get something like this
Table1+2
ID Data Data2
1 r a
2 t x
3 z c
1 u q
and NOT
Table1+2
ID Data Data2
1 r q
2 t x
3 z c
1 u q
and my question is: is there any possibility to tell table 2 that if u have used something for table 1, dont use it and give me next value. do i have to make it im T-SQL or to and new column where i can list if this id exists then write 2 if not 1(Number data). How can i solve this problem?
Ty u in advance.
Since there's no uniqueness in the ID on both tables, lets add some uniqueness to it.
So it can be used to join on.
The window function ROW_NUMBER can be used for that.
An example solution that gives the expected result:
DECLARE #TestTable1 TABLE (ID INT, Data VARCHAR(1));
DECLARE #TestTable2 TABLE (ID INT, Data VARCHAR(1));
INSERT INTO #TestTable1 VALUES (1,'r'),(2,'t'),(3,'z'),(1,'u');
INSERT INTO #TestTable2 VALUES (1,'q'),(1,'a'),(2,'x'),(3,'c');
select
t1.ID, t1.Data,
t2.Data as Data2
from (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable1
) t1
left join (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable2
) t2 on t1.ID = t2.ID and t1.rn = t2.rn;
Note: Because of the LEFT JOIN, this does assume the amount of same ID's in table2 are equal or lower can those on table1. But you can change that to a FULL JOIN if that's not the case.
Returns :
ID Data Data2
1 r a
1 u q
2 t x
3 z c
To get the other result could have been achieved in different ways.
This is actually a more common situation.
Where one wants all from Table 1, but only get one value from Table 2 for each record of Table 1.
1) A top 1 with ties in combination with a order by rownumber()
select top 1 with ties
t1.ID, t1.Data,
t2.Data as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
order by row_number() over (partition by t1.ID, t1.Data order by t2.Data desc);
The top 1 with ties will only show those where the row_number() = 1
2) Using the row_number in a subquery:
select ID, Data, Data2
from (
select
t1.ID, t1.Data,
t2.Data as Data2,
row_number() over (partition by t1.ID, t1.Data order by t2.Data desc) as rn
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
) q
where rn = 1;
3) just a simple group by and a max :
select t1.ID, t1.Data, max(t2.Data) as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
group by t1.ID, t1.Data
order by 1,2;
All 3 give the same result:
ID Data Data2
1 r q
1 u q
2 t x
3 z c
Use ROW_NUMBER
DECLARE #Table1 TABLE (ID INT, DATA VARCHAR(10))
DECLARE #Table2 TABLE (ID INT, DATA VARCHAR(10))
INSERT INTO #Table1
VALUES
(1, 'r'),
(2, 't'),
(3, 'z'),
(1, 'u')
INSERT INTO #Table2
VALUES
(1, 'q'),
(1, 'a'),
(2, 'x'),
(3, 'c')
SELECT
A.*,
B.DATA
FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table1) A INNER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table2) B ON A.ID = B.ID AND
A.RowId = B.RowId

SUM totals from multiple child tables returning inflated total

TABLE1 - RegId (int) pk, RegDate (date), CityId, Tons1(int), Tons2(int)
TABLE2 - RegId(int) fk, OtherDate (date)
TABLE3 - id, City
SELECT c.City
, SUM(t1.Tons1 + t1.Tons2) as 'TotalTons'
FROM Table1 t1
JOIN Table2 t2 ON t1.RegId = t2.RegId
JOIN table3 c ON t1.CityId = c.Id
WHERE YEAR(t2.OtherDate) = '2016'
GROUP BY c.City
If there are multiple matches in table 2, the 'TotalTons' is multipled. If I remove the join it is ok, but I have to have the table2 date for the WHERE clause.
Samples:
TABLE1
(20, '2016-2-2', 3, 2, 3)
(21, '2016-4-12', 7, 3, 5)
(22, '2016-4-12', 7, 6, 3)
TABLE 2
(20, '2016-2-2')
(20, '2016-2-3')
(20, '2016-2-5')
(20, '2016-2-1')
(21, '2016-5-12')
(22, '2016-9-2')
TABLE 3
(3, 'Dallas')
(7, 'Kansas')
RESULTS
City TotalTons
---- ---------
Dallas 20 (should be 5)
Kansas 17 (Correct)
So the Dallas total tons is 5, but because there are 5 different rows in table 2 for RegId 20, it is multiplied 4 times.
How do I get it to stop multiplying the SUM by the t2 results?
You could replace the join with an exists and a sub query:
SELECT c.City
, SUM(t1.Tons1 + t1.Tons2) as 'TotalTons'
FROM Table1 t1
JOIN table3 c ON t1.CityId = c.Id
WHERE EXISTS (
SELECT 1
FROM Table2 t2
WHERE t2.RegId = t1.RegId
AND YEAR(t2.OtherDate) = '2016'
)
GROUP BY c.City
Try This:
DECLARE #TABLE1 TABLE(RegId INT,RegDate DATE, CityId INT, Tons1 INT, Tons2 INT)
INSERT INTO #TABLE1 VALUES
(20, '2016-2-2', 3, 2, 3),(21, '2016-4-12', 7, 3, 5),(22, '2016-4-12', 7, 6, 3)
DECLARE #TABLE2 TABLE(RegId INT,OtherDate DATE)
INSERT INTO #TABLE2 VALUES (20, '2016-2-2'),(20, '2016-2-3'),(20, '2016-2-5'),
(20, '2016-2-1'),(21, '2016-5-12'),(22, '2016-9-2')
DECLARE #TABLE3 TABLE(id INT,City NVARCHAR(10))
INSERT INTO #TABLE3 VALUES (3, 'Dallas'),(7, 'Kansas')
SELECT DISTINCT B.City,B.TotalTons FROM ( SELECT T2.*,CityId FROM #TABLE2 T2
LEFT JOIN #TABLE1 T1 ON T1.RegId = T2.RegId ) A LEFT JOIN (SELECT CityId,City, SUM(Tons1+Tons2) TotalTons
FROM #TABLE3 T3 LEFT JOIN #TABLE1 T1 ON T1.CityId = T3.id GROUP BY CityId,City ) B ON A.CityId = B.CityId
WHERE YEAR(OtherDate) = '2016' -- change your date clause
Hope it helps. :)

How to select two table parent and child hierarchy using joins in sql?

I am try to select parent child hierarchy from two different tables, but am not getting correct output..if any one know tell me..
Table1
Id title
1 a1
2 b
3 c1
4 d1
Table2
Id title pid
1 a null
2 b 1
3 c 2
4 d 1
Check if the table1 id equal to table2 pid then get table1 title.
Output Like
Id title
1 a1
2 a1<b
3 a1<b<c1
4 a1<d1
SELECT T2.PId AS MId, CASE WHEN T2.Id IS NOT NULL THEN T2.Title + '>' + T1. title ELSE T1. title END AS title, T2.Id AS PId
FROM(SELECT T0.Id AS Id, CASE WHEN T1.Id IS NOT NULL THEN T1.Title + '>' + T0. title ELSE T0.title END AS title, T1.PId AS PId
FROM (SELECT T1.PgeId AS MnuId, T1.Title AS title, adnMNU.PId AS PId
FROM TABLE1 T1 join TABLE2 ON T1.Id = TABLE2.Id ) T0
Left JOIN adnPGE T2 ON T0.PId=T1.Id )T1
Left JOIN adnPGE T3 ON T1.PId=T2.Id
I'd use a recursive common table expression (CTE). One note, it doesn't look like you're using the titles from Table2. Just an observation; it doesn't hurt my feelings any. As such, I created a view of the data that uses the Ids from Table2 and the titles from Table1.
with Table1 as (
select * from (values
(1, 'a1'),
(2, 'b '),
(3, 'c1'),
(4, 'd1')
) as x(Id, title)
), Table2 as (
select * from (values
(1, 'a', null),
(2, 'b', 1 ),
(3, 'c', 2 ),
(4, 'd', 1 )
) as x(Id, title, pid)
), combined as (
select b.Id, a.title, b.pid
from Table1 as a
join Table2 as b
on a.Id = b.Id
), r_cte as (
select *, cast(title as varchar(max)) as [path]
from combined
where pid is null
union all
select child.*, cast(concat(parent.[path], '>', child.title) as varchar(max))
from r_cte as parent
join combined as child
on child.pid = parent.Id
)
select *
from r_cte
order by id;

Append parent table column value to child table column in sql

Assume I have two tables, as below:
Table 1
ID Name
------------------
1 Adam
2 Eve
Table 2
ID FK_ID (Table 1) Name
--------------------------------------------
1 1 Chris
2 1 Austin
3 1 Steve
4 2 Charles
5 2 Erik
6 2 Austin
Required table as Result
ID Name
-----------------
1 Chris
2 Austin Adam
3 Steve
4 Charles
5 Erik
6 Austin Eve
Notice here, in the resulting table I want repeated "Austin" appended with "Adam/Eve" from parent table (i.e. Table 1), depending on "FK_ID". I want to do this in SQL. Any idea/help would really be appreciated.
You can use GROUP BY Name to check names with count(*) > 1 and then do a LEFT JOIN to append T1 names appropriately
Sample Data
CREATE TABLE Table1
([ID] int, [Name] varchar(4));
INSERT INTO Table1
([ID], [Name])
VALUES
(1, 'Adam'),
(2, 'Eve');
CREATE TABLE Table2
([ID] int, [FK_ID] int, [Name] varchar(7));
INSERT INTO Table2
([ID], [FK_ID], [Name])
VALUES
(1, 1, 'Chris'),
(2, 1, 'Austin'),
(3, 1, 'Steve'),
(4, 2, 'Charles'),
(5, 2, 'Erik'),
(6, 2, 'Austin');
Query
SELECT
T2.ID,
T2.Name + CASE WHEN T3.Name IS NOT NULL THEN ' ' + T1.Name ELSE '' END as Name
FROM Table2 T2
INNER JOIN Table1 T1 ON T2.[FK_ID] = T1.id
LEFT JOIN (SELECT Name FROM Table2 GROUP BY Name HAVING COUNT(*) > 1) T3 ON T2.Name = T3.Name
Output
ID Name
1 Chris
2 Austin Adam
3 Steve
4 Charles
5 Erik
6 Austin Eve
You can use a window count to determine whether a name is repeated or not:
SELECT t2.ID,
CONCAT(t2.Name,
IIF(COUNT(t2.Name) OVER(PARTITION BY t2.Name) > 1,
COALESCE(' ' + t1.Name, ''),
''))
FROM TABLE2 AS t2
LEFT JOIN TABLE1 AS t1 ON t2.FK_ID = t1.ID
SELECT ID,NAME
FROM TABLE2
WHERE NAME NOT IN (
SELECT NAME
FROM TABLE2
GROUP BY NAME
HAVING COUNT(*) >1 )
UNION
SELECT A.ID AS ID,A.NAME+' '+B.NAME AS NAME
FROM TABLE2 AS A INNER JOIN TABLE1 AS B
ON A.FK_ID = B.ID
WHERE NAME IN (
SELECT NAME
FROM TABLE2
GROUP BY NAME
HAVING COUNT(*) >1 )
ORDER BY ID
Using union to process the logic by two group.

Select record from table1 where value from table2

I have 2 tables here:
table1
id name idfrom idto
1 test 2 3
2 test3 1 9
table2
id branch status
2 a from
1 b from
9 c to
3 d to
How do I select branch from table2 and table1 based on status in table2?
I want the result to look like this:
id name branchfrom branchto
1 test a d
2 test3 b c
I answer it doesn't mean I like it.
SELECT id, name, bfrom.branch branchfrom, bto.branch branchto
FROM table1 t1
INNER JOIN (SELECT id, branch
FROM table2
WHERE status = 'from') bfrom
ON t1.idfrom = bfrom.id
INNER JOIN (SELECT id, branch
FROM table2
WHERE status = 'to') bto
ON t1.idto = bto.id;
I use INNER JOIN as sample only. You must adjust with your requirement (which you didn't clearly specify).
Something like the following should do (assuming you're wanting to join on id in both tables):
select t1.id, t1.name, f.branch as branchfrom, t.branch as branchto
from table1 as t1
join table2 as f
on f.id = t1.id
and f.status = 'from'
join table2 as t
on t.id = t1.id
and t.status = 'to'
This should work for you:
select t1.id, t1.name, f.branch as branchfrom, f1.branch as branchto
from table1 as t1
join table2 as f
on t1.idfrom = f.id
join table2 as f1
on t1.idto = f1.id
Please see here for demo: SQL Fiddle Demo
I don't know if this is better or worse than what the other two people have suggested but
select
t1.name,
(select
t2.branch
from
table2 t2
where
t1.idfrom = t2.id
) as branchfrom,
(select
t2.branch
from
table2 t2
where
t1.idto = t2.id
) as branchto
from
table1 t1
Here is a fiddle
Use this Code:
CREATE TABLE #table1 (
id int,
name varchar(10),
idfrom int,
idto int
)
CREATE TABLE #table2 (
id int,
branch char,
statuss varchar(10)
)
INSERT INTO #table1
VALUES (1, 'test', 2, 3)
INSERT INTO #table1
VALUES (2, 'test3', 1, 9)
INSERT INTO #table2
VALUES (2, 'a', 'From')
INSERT INTO #table2
VALUES (1, 'b', 'From')
INSERT INTO #table2
VALUES (9, 'c', 'to')
INSERT INTO #table2
VALUES (3, 'd', 'to')
SELECT
a.id,
a.name,
(SELECT
b.branch
FROM #table2 b
WHERE a.idfrom = b.id
AND b.statuss = 'FROM')
AS BranchFrom,
(SELECT
b.branch
FROM #table2 b
WHERE a.idto = b.id
AND b.statuss = 'to')
AS BranchTo
FROM #table1 a

Resources