SQL Join multiple values to multiple values - sql-server

I have two tables which I want to join. I have tried LEFT, RIGHT, INNER joins on Table but no success.
Table1
Name1 Name2
------------
A 1
B 2
C 3
D 1
Table2
Name2 Name3
------------
1 x
1 y
2 x
3 x
3 y
3 z
4 y
The result for what I am looking for is:
ResultTable
Name1 Name2 Name3
------------------
A 1 x
A 1 y
B 2 x
C 3 x
C 3 y
C 3 z
D 1 x
D 1 y

You can try this using SQL Server Join Reference.
Select Table1.Name1, Table1.Name2, Table2.Name3
from Table1
inner join Table2 on Table1.Name2 = Table2.Name2
Here is the implementation.
create table Table1 (Name1 varchar(5), Name2 int)
Insert into Table1 Values ('A',1), ('B',2), ('C',3), ('D',1)
create table Table2 (Name2 int, Name3 varchar(5))
Insert into Table2
Values (1, 'x'), (1, 'y'), (2, 'x'), (3, 'x'), (3, 'y'), (3, 'z'), (4, 'y')
Select Table1.Name1, Table1.Name2, Table2.Name3
from Table1
inner join Table2 on Table1.Name2 = Table2.Name2
The output is as shown below.
Name1 Name2 Name3
----------------------
A 1 x
A 1 y
B 2 x
C 3 x
C 3 y
C 3 z
D 1 x
D 1 y
Online Demo

Related

How to Sum particular values in a column in Microsoft SQL Server?

I'm kinda new to this and I have been stuck on this for a while now.
Example:
Col1 Col2 Col3
A | H | 1
A | I | 2
A | J | 3
B | J | 4
B | K | 5
C | L | 6
How can I sum 'Col3' but only for particular values. For example sum up the values in 'Col3' where the letters in 'Col1' are in the same row as 'Col3'. So A = 6 (1+2+3) and B = 9 (4+5) and C = 6
So you get this:
Col1 Col2 Col3
A | H | 6
A | I | 6
A | J | 6
B | J | 9
B | K | 9
C | L | 6
This is what I had so far:
SELECT Col1, Col2, SUM(Col3)
FROM Table1
GROUP BY Col1, Col2;
Thanks
Just to elaborate on my comment.
You can use the window function sum() over()
Example
Declare #YourTable Table ([Col1] varchar(50),[Col2] varchar(50),[Col3] int) Insert Into #YourTable Values
('A','H',1)
,('A','I',2)
,('A','J',3)
,('B','J',4)
,('B','K',5)
,('C','L',6)
Select Col1
,Col2
,Col3 = sum(Col3) over (partition by Col1)
From #YourTable
Returns
Col1 Col2 Col3
A H 6
A I 6
A J 6
B J 9
B K 9
C L 6
Just as another way you can do this way also using join and SUM (Transact-SQL)
function.
create table TestTable (Col1 varchar(5)
, Col2 varchar(5)
, Col3 int)
insert into TestTable Values
('A', 'H', 1),
('A', 'I', 2),
('A', 'J', 3),
('B', 'J', 4),
('B', 'K', 5),
('C', 'L', 6)
SELECT tblA.Col1
,tblA.Col2
,tblB.Col3
FROM (
SELECT Col1
,Col2
FROM TestTable
) tblA
INNER JOIN (
SELECT Col1
,sum(Col3) AS Col3
FROM TestTable
GROUP BY Col1
) tblB ON tblA.Col1 = tblB.Col1
Live Demo
There are a number of ways to write data aggregation queries like this. Which to use depends on what your final results need to look like. Just to go over some basics, I’ll go over several methods here.
The simplest is to use a WHERE clause:
SELECT Col1, sum(Col3)
from MyTable
where Col1 = 'A'
This will produce a single row of data:
Col1 Col3
A | 6
To produce sums for all of the distinct values in ColA, you would use GROUP BY:
SELECT Col1, sum(Col3)
from MyTable
group by Col1
This will produce three rows of data:
Col1 Col3
A | 6
B | 9
C | 6
The above samples are pretty straightforward and basic SQL examples. It is actually a bit difficult to produce the result set from your example, where you include Col2 and show the summation, because Col2 is not part of the data aggregation. Several ways to do this:
Using a subquery:
SELECT
mt.Col1
,mt.Col2
,sub.SumCol3 Col3
from MyTable mt
inner join (select
Col1
,sum(Col3) SumCol3
from MyTable
group by Col1) sub
on sub.Col1 = mt.Col1
Using a common table expression:
WITH cteSub
as (select
Col1
,sum(Col3) SumCol3
from MyTable
group by Col1)
select
mt.Col1
,mt.Col2
,cteSub.SumCol3 Col3
from MyTable mt
inner join cteSub
on ctesub.Col1 = mt.Col1
And, perhaps the most obscure and obtuse, using aggregation fucntions with partitioning:
SELECT
Col1
,Col2
,sum(Col3) over (partition by Col1) Col3
from MyTable
Thorough and complete discussions of all the above tactics (better than anything I'd write) can be found online, by searching for "SQL" plus the appropriate term (aggregation, subquery, CTE, paritioning functions). Good luck!

join with a table but only on certain condition

I need to add a join but only when a certain condition is met.
Let me explain with an example.
I have these 3 tables
Table_I
I_ID Value Ischecked Region
---- ----- --------- ------
1 A 0 N1
2 B 1 N1
3 C 0 N2
Table_PB
PB_ID Region Code
----- ------ ----
1 N1 A1
2 N1 A2
3 N1 A3
4 N2 C1
Table_D
D_ID I_ID PB_ID
---- ---- -----
1 1 1
2 1 2
3 3 1
4 3 4
An my wanted result is this
I_ID Value IsChecked Region PB_ID Code
---- ----- --------- ------ ----- ----
1 A 0 N1 1 A1
1 A 0 N1 2 A2
2 B 1 N1 null A1
2 B 1 N1 null A2
2 B 1 N1 null A3
3 C 0 N2 1 A1
3 C 0 N2 4 C1
How do I got this result ?
Well I start with all rows from Table_I and join them with Table_D and finally with Table_PB
The catch is that when no row is found in Table_D and IsChecked is 1 then I need an extra join on Table_PB that will add all rows with the same Region as found in Table_I
I hope it is clear what I mean.
So what I got now is this
declare #Table_I table (I_ID int, Value varchar(10), Ischecked bit, Region varchar(2))
insert into #Table_I values (1, 'A', 0, 'N1'), (2, 'B', 1, 'N1'), (3, 'C', 0, 'N2')
declare #Table_PB table (PB_ID int, Region varchar(2), Code varchar(2))
insert into #Table_PB values (1, 'N1', 'A1'), (2, 'N1', 'A2'), (3, 'N1', 'A3'), (4, 'N2', 'C1')
declare #Table_D table (D_ID int, I_ID int, PB_ID int)
insert into #Table_D values (1, 1, 1), (2, 1, 2), (3, 3, 1), (4, 3, 4)
select i.I_ID, i.Value, i.IsChecked, i.Region, d.PB_ID, pb.Code
from #Table_I i
left outer join #Table_D d on i.I_ID = d.I_ID
left outer join #Table_PB pb on d.PB_ID = pb.PB_ID
This produces this result
I_ID Value IsChecked Region PB_ID Code
---- ----- --------- ------ ----- ----
1 A 0 N1 1 A1
1 A 0 N1 2 A2
2 B 1 N1 null null
3 C 0 N2 1 A1
3 C 0 N2 4 C1
When you compare this with my wanted result you can see the difference for the row with I_ID = 2
Can this be done and how ?
Try separating the logic between 2 sets with UNION ALL:
-- Records from I that exists on D
SELECT
I.I_ID,
I.Value,
I.Ischecked,
I.Region,
D.PB_ID,
P.Code
FROM
#Table_I AS I
INNER JOIN #Table_D AS D ON I.I_ID = D.I_ID
INNER JOIN #Table_PB AS P ON D.PB_ID = P.PB_ID
UNION ALL
-- Records from I that don't exist on D and are checked
SELECT
I.I_ID,
I.Value,
I.Ischecked,
I.Region,
PB_ID = NULL, --P.PB_ID,
P.Code
FROM
#Table_I AS I
INNER JOIN #Table_PB AS P ON I.Region = P.Region
WHERE
I.Ischecked = 1 AND
NOT EXISTS (SELECT 'no record on D' FROM #Table_D AS D WHERE D.I_ID = I.I_ID)
ORDER BY
1
Results:
I_ID Value Ischecked Region PB_ID Code
1 A 0 N1 1 A1
1 A 0 N1 2 A2
2 B 1 N1 NULL A1
2 B 1 N1 NULL A2
2 B 1 N1 NULL A3
3 C 0 N2 4 C1
3 C 0 N2 1 A1
I'm forcing a NULL on the 2nd set because it matches your desired outcome, but there is a PB_ID you can display here that comes from the #Table_PB table (it's commented on code).
You can use CTE
declare #Table_I table (I_ID int, Value varchar(10), Ischecked bit, Region varchar(2))
insert into #Table_I values (1, 'A', 0, 'N1'), (2, 'B', 1, 'N1'), (3, 'C', 0, 'N2')
declare #Table_PB table (PB_ID int, Region varchar(2), Code varchar(2))
insert into #Table_PB values (1, 'N1', 'A1'), (2, 'N1', 'A2'), (3, 'N1', 'A3'), (4, 'N2', 'C1')
declare #Table_D table (D_ID int, I_ID int, PB_ID int)
insert into #Table_D values (1, 1, 1), (2, 1, 2), (3, 3, 1), (4, 3, 4)
;
WITH cte AS (
SELECT i.I_ID, count(D.PB_ID) as CountD
FROM #Table_I i
LEFT JOIN #Table_D D ON i.I_ID = d.I_ID
GROUP BY i.I_ID
)
SELECT
i.I_ID, i.Value, i.IsChecked, i.Region, d.PB_ID, pb.Code
FROM CTE c
join #Table_I i on i.I_ID = c.i_id
left join #Table_D d on i.I_ID = d.I_ID
left join #Table_PB pb on (d.PB_ID = pb.PB_ID) OR (c.CountD = 0 AND i.isChecked = 1 AND i.Region = pb.Region)

Aggregate count of different columns

There are two table:
Table a:
id name b_id1 b_id2 b_id3 b_id4
1 a 10 8 null null
2 b 3 4 8 10
3 c 10 5 4 null
Table B
b_id title
3 Value3
4 Value4
5 Value 5
8 Value 8
10 Value
Table A has F.K on b_id1,b_id2,b_id3,b_id4 to table B on b_id cloumn,
We are going to group on Title and count it.
something like following result:
Title Count
Value3 1
Value4 2
Value5 1
Value8 2
Value10 3
Null 3
Full working example:
DECLARE #Table1 TABLE
(
[id] INT
,[name] VARCHAR(1)
,[b_id1] INT
,[b_id2] INT
,[b_id3] INT
,[b_id4] INT
);
DECLARE #Table2 TABLE
(
[b_id] INT
,[title] VARCHAR(7)
);
INSERT INTO #Table1 ([id], [name], [b_id1], [b_id2], [b_id3], [b_id4])
VALUES (1, 'a', 10, 8, NULL, NULL)
,(2, 'b', 3, 4, 8, 10)
,(3, 'c', 10, 5, 4, NULL);
INSERT INTO #Table2 ([b_id], [title])
VALUES (3, 'Value3')
,(4, 'Value4')
,(5, 'Value 5')
,(8, 'Value 8')
,(10, 'Value');
SELECT T2.[title]
,COUNT(*)
FROM
(
SELECT [b_id1]
FROM #Table1
UNION ALL
SELECT [b_id2]
FROM #Table1
UNION ALL
SELECT [b_id3]
FROM #Table1
UNION ALL
SELECT [b_id4]
FROM #Table1
) DS
LEFT JOIN #Table2 T2
ON DS.[b_id1] = T2.[b_id]
GROUP BY T2.[title];
One way would be like below:Demo Here
;with cte
as
(select title,count(b_id) as val from table1 t
join
table2 t2
on t.id=t2.b_id
or
t.b_id1=t2.b_id
or
t.b_id2=t2.b_id
or
t.b_id3=t2.b_id
or
t.b_id4=t2.b_id
group by title
)
select * from cte
union all
select null,sum(case
when b_id1 is null then 1 else 0 end)+
sum(case
when b_id2 is null then 1 else 0 end)
+sum(case
when b_id3 is null then 1 else 0 end)
+sum(case
when b_id4 is null then 1 else 0 end)
from table1
output:
Value 3
Value 5 1
Value 8 2
Value3 2
Value4 2
NULL 3

SQL group by xml path columns

Tables:
Table 1(unique values):
id | NameDep
15 X
16 Y
Table 2:
id id_department id_resource
1 15 45
2 15 47
3 16 99
....
Table 3(about 400records):
id_resource resourceName
45 name1
47 name2
99 name3
Table 4(contains multiple columns, but we only need for joining this one):
id_shift
185281
185282
185283
185284
Table 5(shiftName = table3.resourceName):
id_shift shiftName id_department
185281 name1 15
185282 name1 15
185283 name2 15
185284 name2 15
My current output is:
NameDep id resourceName shifts
X 15 name1 185281,185282,185283,185284
X 15 name2 185281,185282,185283,185284
X 15 name3 185281,185282,185283,185284
X 15 name4 185281,185282,185283,185284
X 15 name5 185281,185282,185283,185284
...etc
So basically, in resourceName i have all the data from table3
My goal is to group the shifts with the resourceName.
In my exemple 182581 and 182582 belong to resourceName = name1.
182583 and 182584 to resourceName = name2
So, I would like my output to be like this.
Desired output:
NameDep id resourceName shifts
X 15 name1 185281,185282
X 15 name2 185283,185284
...so on
I've used a temporary table, because I've read it's the only way to have a join between tables and results in xml path as I need. I can't figure out how to change the join / or use the XML PATH in order to have my desired output.
My current SQL IS:
select NameDep as region,d.id,r.resourceName
into #deps from table1 d
join table2 rd on rd.id_department=d.id
join table3 r on r.id_resource=rd.id_resource
where NameDep = 'x';
select SUBSTRING( ( select (',' + CONVERT(VARCHAR(MAX),table5.id_shift))
from table4
join table5 on table5.id_shift =table4 .id_shift
join #deps on #deps.id_department=table5.id_department
and #deps.resourceName=table5.shiftName
FOR XML PATH( '' ) ) , 2, 100000) as trip3 into #Planned
select * from (
select *
from
#deps,#Planned
) t
drop table #Planned,#deps
Thank you
You can use STUFF without using a subquery. STUFF is usually a good place to start when you're trying to compile a delimited list. Also, you don't need table 4.
In this case, you can use ',' + shift FOR XML PATH('') to create a list of comma-delimited shifts grouped by department and resource:
,185281,185282
And then you can use STUFF to chop off the first character and replace it with an empty string, leaving you with:
185281,185282
declare #t1 table (id int, namedep varchar(1))
insert into #t1 values (15, 'X')
insert into #t1 values (16, 'Y')
declare #t2 table (id int, id_department int, id_resource int)
insert into #t2 values (1, 15, 45)
insert into #t2 values (1, 15, 47)
insert into #t2 values (1, 16, 99)
declare #t3 table (id_resource int, resourceName varchar(10))
insert into #t3 values (45, 'name1')
insert into #t3 values (47, 'name2')
insert into #t3 values (99, 'name3')
declare #t5 table (id_shift int, shiftName varchar(10), id_department int)
insert into #t5 values (185281, 'name1', 15)
insert into #t5 values (185282, 'name1', 15)
insert into #t5 values (185283, 'name2', 15)
insert into #t5 values (185284, 'name2', 15)
select t1.namedep, t1.id, t3.resourceName,
stuff(
(select ',' + cast(id_shift as varchar(10))
from #t5
where shiftName = t3.resourceName and id_department = t1.id
for xml path('')), 1, 1, '') as shifts
from #t1 t1
inner join #t2 t2 on t1.id = t2.id_department
inner join #t3 t3 on t2.id_resource = t3.id_resource
Output:
namedep id resourceName shifts
X 15 name1 185281,185282
X 15 name2 185283,185284
...etc

Select Minimum difference of summary of two columns in T-SQL

I need help building a query, which returns the Minimum difference between Value + another Value from same table and the other ID that gave the result (plus the sum can't be sum of the value itself)
Table:
ID Value
1 1
2 2
3 5
4 -10
5 -5
6 3
7 -15
Expected result:
ID Value MinDif IDofTheOtherValue
1 1 3 2 <-- MinDif = 1 + 2 (ID 1 + ID 2)
2 2 3 1 <-- MinDif = 2 + 1 (ID 2 + ID 1)
3 5 0 5 <-- MinDif = 5 + -5 (ID 3 + ID 5)
4 -10 -5 3 <-- MinDif = -10 + 5 (ID 4 + ID 3)
5 -5 0 3 <-- MinDif = -5 + 5 (ID 5 + ID 3)
6 3 -2 5 <-- MinDif = 3 + -5 (ID 6 + ID 5)
7 -15 -10 3 <-- MinDif = -15 + 5 (ID 7 + ID 3)
Here's a query to create the table:
DECLARE #myTable TABLE(ID int, Value int)
INSERT INTO #myTable VALUES (1, 1), (2,2), (3, 5), (4, -10), (5, -5), (6, 3), (7, -15)
And here's what I have tried, but this gives an SQL error (Cannot perform an aggregate function on an expression containing an aggregate or a subquery.)
SELECT m.ID, MIN(ABS(m.Value + (SELECT m2.Value FROM #myTable m2)))
FROM #myTable m
This is giving your required results:
with diffRank as
(
select ID = t1.ID
, minDif = t1.value + t2.value
, IDofTheOtherValue = t2.ID
, diffRank = row_number() over (partition by t1.ID order by abs(t1.value + t2.value), t2.ID)
from #myTable t1
inner join #myTable t2 on t1.ID <> t2.ID
)
select ID
, minDif
, IDofTheOtherValue
from diffRank
where diffRank = 1
order by ID;
SQL Fiddle with demo.
I resolved this by myself. Here's the Select clause:
SELECT tab.ID, tab.Value, test.*
FROM #myTable tab
OUTER APPLY
(SELECT TOP 1 ID AS [AnotherID], [SUM]
FROM
(
SELECT m.ID, m2.ID AS [ID2], m.Value + m2.Value AS [SUM]
FROM #myTable m
JOIN #myTable m2 ON m2.ID <> m.ID
) apu WHERE ID2 = tab.ID ORDER BY ABS([SUM])) test
In Oracle I would do:
select x.id, (select min(abs(x.value + y.value)) from my_table y),
(select first value (y.id) over (order by abs(x.value + y.value))
from my_table y)
from my_table x
think of something similar in TSQL
Try this.. it should work.
DECLARE #myTable TABLE(ID int, Value int)
INSERT INTO #myTable VALUES (1, 1), (2,2), (3, 5), (4, -10), (5, -5), (6, 3), (7, -15)
SELECT C.ID, C.Value
, C.Value + (SELECT TOP 1 E.Value FROM #myTable E WHERE C.AbsMinDif = ABS(C.Value + E.Value) ORDER BY E.ID) MinDif
, (SELECT TOP 1 F.ID FROM #myTable F WHERE C.AbsMinDif = ABS(C.Value + F.Value) ORDER BY F.ID) IDofTheOtherValue
FROM (
SELECT A.ID, MIN(A.Value) Value, MIN(ABS(A.Value + B.Value)) AbsMinDif
FROM #myTable A
CROSS JOIN #myTable B
WHERE A.ID <> B.ID
GROUP BY A.ID
) C

Resources