Pivot data using multiple join - sql

I have a table like this in SQL Server
name
tems
aver
a
1
12
a
2
13.5
b
1
19
b
2
15.5
c
2
5
d
1
16.75
How should I have following form with join clause? Not with group by, CTE, COALESCE, Null functions, conditional aggregation and PIVOT. Case clause in only allowed in join conditions.
select columns
from Table T1
join Table T2 on .......................
join Table T3 on .......................
name
aver1
aver2
a
12
13.5
b
19
15.5
b
Null
5
d
16.75
Null

I would recommend conditional aggregation:
select name,
max(case when tems = 1 then aver end) as aver1,
max(case when tems = 2 then aver end) as aver2
from mytable
group by name
If you insist on joining, then:
select t1.name, t1.aver as aver1, t2.aver as aver2
from mytable t1
inner join mytable t2 on t2.name = t1.name
and t1.tems = 1 and t2.tems = 2
One caveat with the above join technique is that it eliminates names that do not have both tems, so it would not produce the result you want for your sample data. You could work around this with a full join, but that would incur additional complexity. The conditional aggregation is simpler.

..for join...
declare #t table(name varchar(10), tems smallint, aver decimal(9,2));
insert into #t(name, tems, aver)
values
('a', 1, 12),
('a', 2, 13.5),
('b', 1, 19),
('b', 2, 15.5),
('c', 2, 5),
('d', 1, 16.75);
select coalesce(t1.name, t2.name) as name, t1.aver as aver1, t2.aver as aver2
from
(
select *
from #t
where tems = 1
) as t1
full outer join
(
select *
from #t
where tems = 2
) as t2 on t1.name = t2.name;
select coalesce(t1.name, t2.name) as name, t1.aver as aver1, t2.aver as aver2
from #t as t1
full outer join #t as t2 on t1.name = t2.name and t1.tems = 1 and t2.tems = 2
where t1.tems = 1
or
(t1.tems is null and t2.tems=2);
select t3.name, t1.aver as aver1, t2.aver as aver2
from #t as t1
full outer join #t as t2 on t1.name = t2.name and t1.tems = 1 and t2.tems = 2
join #t as t3 on case when t1.name is null then t2.name else t1.name end = t3.name
and case when t1.name is null then 2 else 1 end = t3.tems
where t1.tems = 1
or
t1.tems is null and t2.tems=2;

Related

SQL Server Join two tables without duplicates in primary table

I have two tables that I want to join together such that all foreign rows are returned and the primary table's rows are not duplicated. For example:
T1
pk code value
1 One 100
2 Two 200
T2
fk value
1 10
1 15
1 30
2 25
I want all records of T2 without the T1 records duplicating, so the result set I want to look like this:
T2.fk T1.code T1.value T2.value
1 One 100 10
1 NULL NULL 15
1 NULL NULL 30
2 Two 200 25
Is there a SQL Server join method for achieving that?
You need to rank your rows in T2 and do a left join including rank as a join condition:
with cte as(select *, row_number() over(partition by fk order by value) as rn from T2)
select c.fk, t.code, t.value, c.value
from cte c
left join T1 t on c.fk = t.pk and c.rn = 1
Here is the full example:
DECLARE #t1 TABLE
(
pk INT ,
code VARCHAR(MAX) ,
value INT
)
INSERT INTO #t1
VALUES ( 1, 'One', 100 ),
( 2, 'Two', 200 )
DECLARE #t2 TABLE ( fk INT, value INT )
INSERT INTO #t2
VALUES ( 1, 10 ),
( 1, 15 ),
( 1, 30 ),
( 2, 25 );
WITH cte
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY fk ORDER BY value ) AS rn
FROM #t2
)
SELECT c.fk ,
t.code ,
t.value ,
c.value
FROM cte c
LEFT JOIN #t1 t ON c.fk = t.pk
AND c.rn = 1
Try this:
select T2.fk,
CASE
WHEN (SELECT COUNT(*) FROM t2 tother WHERE tother.fk = t2.fk
AND tother.value > t2.value) > 0 THEN NULL ELSE t1.code
END,
CASE
WHEN (SELECT COUNT(*) FROM t2 tother WHERE tother.fk = t2.fk
AND tother.value > t2.value) > 0 THEN NULL ELSE t1.value
END,T2.value
from t2
join t1
on t2.fk = t1.pk
DECLARE #t1 TABLE (pk int,code varchar(10),value int)
DECLARE #t2 TABLE (fk int,value int)
INSERT INTO #t1
SELECT 1,'one',100
UNION
SELECT 2,'two',200
INSERT INTO #t2
SELECT 1,10
UNION SELECT 1,15 UNION SELECT 1,30 UNION SELECT 2,25
;WITH cte AS(
SELECT t2.fk,t2.value t2val,t1.pk,t1.code,t1.value t1val,ROW_NUMBER() OVER(PARTITION BY fk ORDER BY fk) rno FROM #t2 t2 LEFT JOIN #t1 t1 on t2.fk=t1.pk)
SELECT fk,code=(CASE WHEN rno=1 THEN code ELSE null END),t1val=(CASE WHEN rno=1 THEN t1val ELSE NULL END),t2val FROM cte
output
fk code t1val t2val
1 one 100 10
1 NULL NULL 15
1 NULL NULL 30
2 two 200 25

SQL query with one to many relation

I have following table
Table1
id name col1 col2 col3 col4
-----------------------------------
1 test 1.1 1.2 1.3 1.4
2 test2 2.1 2.2 2.3 2.4
Table2
id fk_table1 amt type(fk_table3)
-----------------------------------
1 1 2 1
2 1 3 1
3 1 9 2
4 2 1 1
and I want to query such that I have get below result
id | name | total_type1_amt |total_type2_amt | col1 col2 col3 col4
-----------------------------------------------------------------------
1 test 5 (2+3) 9 1.1 1.2 1.3 1.4
2 test2 1 0 2.1 2.2 2.3 2.4
Basically in result I want group by table1.id with added columns for total_typeX_amt, there will be millions of rows in table1 and table2 so basically looking for optimized way to do it.
SELECT t1.id,
t1.name,
t2.total_type1_amt,
t2.total_type2_amt
FROM table1 t1
INNER JOIN
(
SELECT fk_table1,
SUM(CASE WHEN type = 1 THEN amt END) AS total_type1_amt,
SUM(CASE WHEN type = 2 THEN amt END) AS total_type2_amt
GROUP BY fk_table1
) t2
ON t1.id = t2.fk_table1
If you need this to run fast, you can try creating a view using the subquery (which I called t2 above), with an index on the fk_table1 column. Assuming that table1 also has an index on id, then the join should run reasonably fast.
It's not 100% your desired result, but you could try something like
select fk_table1, type, sum(amt)
from table1
inner join table2 on table1.id = table2.fk_table1
group by fk_table1, type
which should lead to something like
fk_table1 | type | sum
1 1 5
1 2 9
2 1 1
try dis to get total for total_type1_amt
select table1.id, table2.name ,(select count(table2.amt) as total_type1_amt where table1.id = table2.fk_table1 from table.1) from table1
inner join table2 on table1.id = table2.fk_table1
group by table.id
SELECT
T1.id,
T1.name,
SUM(CASE T2.type WHEN 1 THEN T2.amt ELSE 0 END) AS total_type1_amt,
SUM(CASE T2.type WHEN 2 THEN T2.amt ELSE 0 END) AS total_type2_amt
FROM #tbl1 T1
LEFT JOIN #tbl2 T2 ON T1.id=T2.fk_table1
GROUP BY T1.id,T1.name
Output:
You can try like this
;WITH cte
AS (SELECT
fk_table1, SUM([1]) total_type1_amt, COALESCE(SUM([2]), 0) total_type2_amt
FROM #table1 PIVOT (MAX(amt) FOR type IN ([1], [2])) p
GROUP BY fk_table1)
SELECT
t.id, t.name, c.total_type1_amt, c.total_type2_amt
FROM #table1 t
LEFT JOIN cte c
ON t.id = c.fk_table1
There at least 2 ways:
SELECT t1.id,
t1.name,
COALESCE(SUM(CASE WHEN [type] = 1 THEN amt END),0) AS total_type1_amt,
COALESCE(SUM(CASE WHEN [type] = 2 THEN amt END),0) AS total_type2_amt,
col1,
col2,
col3,
col4
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.fk_table1
GROUP BY t1.id, t1.name, col1, col2, col3, col4
Another:
SELECT *
FROM (
SELECT t1.id,
t1.name,
t2.[type],
SUM(t2.amt) as sum
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.fk_table1
GROUP BY t1.id, t1.name, t2.[type]
) as t
PIVOT (
MAX(sum) FOR type IN ([1],[2])
) as pvt

Finding unmatched rows of 2 tables in SQL

I have 2 tables with 3 columns that are suppose to have the same information.
I would like have a query that selects only the rows that don't have a complete row match. Below is an example of the 2 tables I would like to match:
Table 1
ID FPRICE FPRODUCT
1 1 A
2 2 B
3 3 C
4 4 D
5 5 F
Table 2
ID TPRICE TPRODUCT
1 1 A
2 2 B
3 3 C
4 5 D
6 6 E
Desired Output:
ID FPRICE FPRODUCT TPRICE TPRODUCT
4 4 D 5 D
5 5 F NULL NULL
6 NULL NULL 6 E
Easier to verify if we build some DDL and fillwith sample data, but I think this would do it. It takes a full join to find records with a partial match and then filters out records with a full match.
sqlfiddle.com
CREATE TABLE Table1 (ID INT, FPRICE INT, FPRODUCT CHAR(1))
INSERT INTO Table1 (ID,FPRICE,FPRODUCT) VALUES
(1, 1, 'A')
,(2, 2, 'B')
,(3, 3, 'C')
,(4, 4, 'D')
,(5, 5, 'F')
CREATE TABLE TABLE2 (ID INT, TPRICE INT, TPRODUCT CHAR(1))
INSERT INTO Table2 (ID,TPRICE,TPRODUCT) VALUES
(1, 1, 'A')
,(2, 2, 'B')
,(3, 3, 'C')
,(4, 5, 'D')
,(6, 6, 'E')
SELECT *
FROM Table1 t1
FULL JOIN
Table2 t2 ON t1.ID = t2.ID
--EDIT: remove to exactly match the desired output
--OR t1.FPRICE = t2.TPRICE
--OR t1.FPRODUCT = t2.TPRODUCT
WHERE NOT ( t1.ID = t2.ID
AND t1.FPRICE = t2.TPRICE
AND t1.FPRODUCT = t2.TPRODUCT)
OR ( COALESCE(t1.ID,t1.FPRICE,T1.FPRODUCT) IS NULL
OR COALESCE(t2.ID,t2.TPRICE,T2.TPRODUCT) IS NULL)
Use full join:
select *
from Table1 t1
full join Table2 t2 on t1.ID = t2.ID and
t1.FPRICE = t2.FPRICE /*and
t1.FPRODUCT = t2.FPRODUCT*/
where t1.ID is null or t2.ID is null
It looks like you want to match on the id's and then compare the values:
select t1.id, t1.fprice, t1.fproduct, t2.fprice, t2.fproduct
from Table1 t1 full join
Table2 t2
on t1.ID = t2.ID
where (t1.ID is null or t2.ID is null) or -- no match as all
(t1.FPRICE <> t2.FPRICE or
t1.FPRODUCT <> t2.FPRODUCT
);
The where clause is a bit more complicated if the column values could be NULL, but your sample data does not have any examples of NULLs.
SELECT *
FROM Table1 t1
FULL JOIN Table2 t2 ON t2.ID = t1.ID
WHERE
t2.TPRICE <> t1.FPRICE
OR t2.TPRODUCT <> t1.FPRODUCT
OR t2.TPRICE IS NULL
OR t1.FPRICE IS NULL
OR t2.TPRODUCT IS NULL
OR t1.FPRODUCT IS NULL
Can you try this query?
SELECT DISTINCT t1.ID t1.FPRICE t1.FPRODUCT
from Table1 t1 LEFT JOIN Table2 ON
Table1.ID = Table2.ID AND Table1.FPRICE = Table2.TPRICE AND Table1.FPRODUCT= Table2.TPRODUCT
WHERE
Table1.ID is NULL or Table1.FPRICE is NULL or Table1.FPRODUCT is NULL
or Table2.TPRICE is NULL or Table2.TPRODUCT is NULL
I would format the answer from Michael1105 as:
SELECT ISNULL(t1.ID,t2.ID),t1.FPRICE,t1.FPRODUCT,t2.TPRICE,t2.TPRODUCT FROM Table_1 t1 FULL JOIN Table_2 t2 ON t2.ID = t1.ID WHERE t2.TPRICE
<>t1.FPRICE OR t2.TPRODUCT
<>t1.FPRODUCT OR t2.TPRICE IS NULL OR t1.FPRICE IS NULL OR t2.TPRODUCT IS NULL OR t1.FPRODUCT IS NULL
for your exact requirement.

MS Access - alternative to performing a "full join" for columns with same name

I have this problem using access: I am using the RIGHT + LEFT outer joins to overcome the fact that ACCESS does not support the FULL JOIN.
SELECT *
FROM T1 RIGHT OUTER JOIN T2
ON T1.xxx = T2.xxx
UNION
SELECT *
FROM T1 LEFT OUTER JOIN T2
ON T1.xxx = T2.xxx
on these tables:
T1:
ID1 | xxx | fieldA
a 1 X
b 2 Y
c 3 Z
T2:
ID2 | xxx | fieldB
d 2 K
e 3 J
f 4 H
AS a result I obtain a table with this structure
T1.xxx | T2.xxx | fieldA | fieldB | ID1 | ID2
1 X a
2 2 Y K b d
3 3 Z J c e
4 H f
xxx is not primary key but has the same name and numerical type (integer)
I saw from many other places that this should work by collapsing the two tables! Here it does not (the elements on the same rows, when non blank, are of course the same)
MY EXPECTATION
FINAL TABLE:
xxx | ID1 | ID2 |fieldA | fieldB
1 a X
2 b d Y K
3 c e Z J
4 f H
It seems that there are different set of column values for both of these 2 tables, you could be having t1.xxx and t2.xxx which have the same values, but other columns dont, the union in this case wouldn't combine these 2 records
Try something like
SELECT T1.xxx
FROM T1 RIGHT OUTER JOIN T2
ON T1.xxx = T2.xxx
UNION
SELECT T2.xxx
FROM T1 LEFT OUTER JOIN T2
ON T1.xxx = T2.xxx
Something like this should give you all the xxx values from table 1 and table 2, ignoring duplicate values for xxx.
The most likely explanation for the behavior you observe is that the rows being returned that are not identical, they are not exact duplicates.
The UNION operator will remove duplicate rows, but it doesn't do anything to "collapse tables" other than that.
To get the specified result set (in the updated question), here's one SQL pattern that would return the result. (I don't know if Access supports this, but this would work in MySQL, SQL Server, Oracle, etc.)
SELECT i.xxx
, v1.ID AS ID1
, v2.ID AS ID2
, v1.fieldA AS fieldA
, v2.fieldB AS fieldB
FROM (
SELECT t1.xxx AS xxx
FROM T1 t1
UNION
SELECT t2.xxx
FROM T2 t2
) i
LEFT
JOIN T1 v1
ON v1.xxx = i.xxx
LEFT
JOIN T2 v2
ON v2.xxx = i.xxx
(NOTE: if xxx is not guaranteed to be unique in each table, then this query could generate duplicate rows.)
MY SOLUTION (absolutely not elegant):
SELECT [QUERY1].XX, ID1, ID2, fieldA, fieldB
FROM (T2 RIGHT JOIN [QUERY1] ON T2.xxx = [QUERY1].XX) LEFT JOIN T1 ON [QUERY1].XX = T1.XX;
where QUERY1 is the following:
(SELECT CInt(NZ(T1.xxx,T2.xxx)) as XX
FROM T1 RIGHT OUTER JOIN T2
ON T1.xxx = T2.xxx
UNION
SELECT CInt(NZ(T1.xxx,T2.xxx)) as XX
FROM T1 LEFT OUTER JOIN T2
ON T1.xxx = T2.xxx)
UNION (SELECT CInt(NZ(T2.xxx,T1.xxx)) as xx1
FROM T1 RIGHT OUTER JOIN T2
ON T1.xxx = T2.xxx
UNION
SELECT CInt(NZ(T2.xxx,T1.xxx)) as xx1
FROM T1 LEFT OUTER JOIN T2
ON T1.xxx = T2.xxx);
notice that xx1 is not used, and not even shown in the table
TRY THIS ONE: I have tried and get both result sets.
DECLARE #T1 TABLE
(
ID1 VARCHAR(2) ,XXX INT, FIELDA VARCHAR(2)
)
DECLARE #T2 TABLE
(
ID2 VARCHAR(2) ,XXX INT, FIELDB VARCHAR(2)
)
INSERT INTO #T1 VALUES
('a', 1, 'X'),
('b', 2, 'Y'),
('c', 3, 'Z')
INSERT INTO #T2 VALUES
('d', 2, 'k'),
('e', 3, 'j'),
('f', 4, 'h')
SELECT
ISNULL(CONVERT(VARCHAR(1),T1.xxx),' ') AS [T1.xxx] ,
ISNULL(CONVERT(VARCHAR(1),T2.xxx),'') AS [T2.xxx] ,
ISNULL(fieldA,'') AS [fieldA],
ISNULL(fieldB,'') AS [fieldB] ,
ISNULL(ID1,'') AS [ID1] ,
ISNULL(ID2,'') AS [ID2]
FROM
(
SELECT XXX as XXX1 FROM #T1
UNION
SELECT XXX as XXX1 FROM #T2
)T LEFT OUTER JOIN
#T1 T1 ON T.XXX1 = T1.XXX
LEFT OUTER JOIN
#T2 T2 ON T.XXX1 = T2.XXX
----------------------RESULT------------------
T1.xxx T2.xxx ID1 ID2 fieldA fieldB
1 a X
2 2 b d Y k
3 3 c e Z j
4 f h
SELECT
ISNULL(CONVERT(VARCHAR(1),T.xxx1),' ') AS [xxx] ,
ISNULL(ID1,'') AS [ID1] ,
ISNULL(ID2,'') AS [ID2],
ISNULL(fieldA,'') AS [fieldA],
ISNULL(fieldB,'') AS [fieldB]
FROM
(
SELECT XXX as XXX1 FROM #T1
UNION
SELECT XXX as XXX1 FROM #T2
)T LEFT OUTER JOIN
#T1 T1 ON T.XXX1 = T1.XXX
LEFT OUTER JOIN
#T2 T2 ON T.XXX1 = T2.XXX
----------------------RESULT------------------
xxx ID1 ID2 fieldA fieldB
1 a X
2 b d Y k
3 c e Z j
4 f h

TSQL Query to fetch row details of a table having the maximum value for a column

I have two tables.
Table 1
Num
1
2
3
Table 2
Num Date Amount
1 12/31 30
1 12/30 31
1 12/29 20
2 12/31 100
2 12/30 90
3 12/31 12
4 11/1 1
Now my result should have
Num Date Amount
1 12/31 30
2 12/31 100
3 12/31 12
(for the 'Num' values in table 1, join with table2 where the date is the most recent)
I am trying to write a tsql query to achieve this.
Any help is appreciated. Thanks
If you want the most recent date for each table1 num individually:
with maxdates as (
select T1.num, max(T2.date) as date
from table2 T2
join table1 T1
on T2.num = t1.num
group by T1.num
)
select t2.num, t2.date, t2.amount
from table2 T2
join maxdates M
on T2.num = M.num
and T2.date = M.date
or if you want the most recent date for all matching records in the table:
with maxdate as (
select max(T2.date) as date
from table2 T2
join table1 T1
on T2.num = t1.num
)
select t2.num, t2.date, t2.amount
from table2 T2
join table1 T1
on T2.num = T1.num
join maxdate M
on T2.date = M.date
Try this query:
SELECT b.*
FROM Table1 a INNER JOIN
(
SELECT a.*,
ROW_NUMBER() OVER(PARTITION BY a.Num ORDER BY Date DESC) rnk
FROM Table2 a
) b
ON a.Num = b.Num
AND rnk = 1
Try this
select t2.* from table2 as t2 inner join table1 as t1 on t2.num=t2.num
inner join
(
Select t2.num,max(t2.amount) as amount from table2
group by t2.num
) as t3 on t2.amount=t3.amount
DECLARE #t1 TABLE(num INT)
DECLARE #t2 TABLE(num INT, [date] NVARCHAR(5), amount INT)
INSERT INTO #t1 (num) VALUES (1),(2),(3)
INSERT INTO #t2 (num,[date],amount) VALUES (1,'12/31',30)
INSERT INTO #t2 (num,[date],amount) VALUES (1,'12/30',31)
INSERT INTO #t2 (num,[date],amount) VALUES (1,'12/29',20)
INSERT INTO #t2 (num,[date],amount) VALUES (2,'12/31',100)
INSERT INTO #t2 (num,[date],amount) VALUES (2,'12/30',90)
INSERT INTO #t2 (num,[date],amount) VALUES (3,'12/31',12)
INSERT INTO #t2 (num,[date],amount) VALUES (4,'11/01',1)
SELECT t2.num,t2.[date],amount
FROM #t1 t1 JOIN #t2 t2 ON t1.num = t2.num
WHERE t2.[date] in (
SELECT MAX(t3.[date])
FROM #t2 t3
WHERE t3.num = t2.num
)
ORDER BY t2.num