join with a table but only on certain condition - sql-server

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)

Related

SQL Join multiple values to multiple values

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

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

Return row only if one occurance

I'm trying to fetch all the Id rows that only has zeroes.
Id | valid
---| ------
1 | 0
1 | 1
2 | 0
2 | 0
3 | 1
4 | 0
4 | 1
5 | 0
6 | 1
SELECT ID
FROM myTable
WHERE Valid = 0
AND ID NOT IN (
SELECT ID
FROM myTable
WHERE Valid != 0
)
Yet another option
Select A.*
From YourTable A
Join (
Select ID
From YourTable
Group By ID
Having min(valid)=0 and max(valid)=0
) B
on A.ID=B.ID
Returns
Id valid
2 0
2 0
5 0
You can use LEFT JOIN with NULL check to achieve your expected result.
Sample execution with given data:
DECLARE #TestTable TABLE (Id int, valid int);
INSERT INTO #TestTable (Id, valid)
VALUES (1, 0), (1, 1), (2, 0), (2, 0), (3, 1), (4, 0), (4, 1), (5, 0), (6, 1);
SELECT T1.*
FROM #TestTable T1
LEFT JOIN #TestTable T2 ON T2.Id = T1.Id AND T2.valid <> 0
WHERE T2.Id IS NULL
Result:
Id valid
----------
2 0
2 0
5 0
If you want Ids only, use SELECT DISTINCT T1.Id.
If your Id has only zeroes that sum of valid would be zero. Here is an example of the code.
drop table if exists dbo.Table03;
create table dbo.Table03 (
Id int
, valid int
);
insert into dbo.Table03 (Id, valid)
values (1, 0), (1, 1), (2, 0), (2, 0), (3, 1), (4, 0), (4, 1), (5, 0), (6, 1);
select
t.Id
from dbo.Table03 t
group by t.Id
having SUM(t.valid) = 0

How can I recursively calculate a value

I have this table.
Bundles
id | parent_id | quantity
1 | 0 | 1
2 | 1 | 4
3 | 2 | 5
I want to get the total quantity of a bundle with id 3, which is 1 * 4 * 5 = 20 items
Can this be done with a single query?
Here's a solution using CTE:
Setup:
CREATE TABLE Table1
(id int, parent_id int, quantity int)
;
INSERT INTO Table1
(id, parent_id, quantity)
VALUES
(1, 0, 1),
(2, 1, 4),
(3, 2, 5),
(4, 0, 7),
(5, 4, 10)
;
CTE to return total of id=3 and it's parent items:
;WITH myCTE AS
(
SELECT id, parent_id, quantity
FROM Table1
WHERE id = 3
UNION ALL
SELECT T.id, T.parent_id, T.quantity
FROM Table1 T
JOIN myCTE C ON T.id = C.parent_id
)
SELECT EXP(sum(log(quantity)))
FROM myCTE
Demo SQL Fiddle
Multiplication method for values in a column, SELECT EXP(sum(log(quantity))), taken from here.

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