SQL join - duplicate rows - sql

I have three tables (simplified version - the whole picture is a bit more complex).
TABLE: CUSTOMER TABLE: PURCHASE1 TABLE: PURCHASE2
=============== ======================= =======================
CustomerID CustomerID | ProductID CustomerID | ProductID
--------------- ------------|---------- ------------|----------
1 1 | 51 1 | 81
2 1 | 52 1 | 82
3 2 | 52 1 | 83
I know the table structure isn't the best but that's not what I need help with. The products held in the purchase tables are of different types, if that helps to provide context.
I'm trying to join the tables, using a query like this:
Select
customer.customerid, purchase1.productid as P1,
purchase2.productid as P2
From
customer
Left join
purchase1 on customer.customerid = purchase1.customerid
Left join
purchase2 on customer.customerid = purchase2.customerid
Where
customer.customerid = 1;
This produces the following:
CustomerID | P1 | P2
--------------------
1 | 51 | 81
1 | 51 | 82
1 | 51 | 83
1 | 52 | 81
1 | 52 | 82
1 | 52 | 83
How do I get it to do this instead?
CustomerID | P1 | P2
-----------|------|---
1 | 51 | null
1 | 52 | null
1 | null | 81
1 | null | 82
1 | null | 83
The first table has a row for every combination of P1 and P2. The second table only has a row for each customer-product combination.
Can I do this without using UNION? The reason I ask, is that because the query will become more complex, using columns from other rows that aren't in PURCHASE1 or PURCHASE2.
If I have to use UNION, how can I do it such that I can still select from other tables and have additional columns in my query?

Use Union . See DEMO. In union, you have to have same number of columns in both queries so use NULL to match number of column in both query
Select * from (Select customer.customerid, purchase1.productid as P1, NULL as P2
from customer
INNER join purchase1
on customer.customerid = purchase1.customerid
UNION ALL
Select customer.customerid, NULL as P1, purchase2.productid as P2
from customer
INNER join purchase2
on customer.customerid = purchase2.customerid) tb
where tb.customerid = 1;

I would do it this way:
select customerid, p1, p2
from customer
left join (
select customerid, productid p1, null p2 from purchase1
union all
select customerid, null p1, productid p2 from purchase2
) using (customerid)
where customerid = 1;
SQLFiddle demo
Now you can attach rest of tables without repeated logic.

I would first of all union up all the tables and then join them to the customer table - like so:
with customer as (select 1 customerid, 'bob' name from dual union all
select 2 customerid, 'ted' name from dual union all
select 3 customerid, 'joe' name from dual),
purchase1 as (select 1 customerid, 51 productid from dual union all
select 1 customerid, 52 productid from dual union all
select 2 customerid, 52 productid from dual),
purchase2 as (select 1 customerid, 81 productid from dual union all
select 1 customerid, 82 productid from dual union all
select 1 customerid, 83 productid from dual),
-- end of mimicking your table and data; main query is below:
purchases as (select customerid, productid productid1, null productid2
from purchase1
union all
select customerid, null productid1, productid productid2
from purchase2)
select c.customerid,
c.name,
p.productid1,
p.productid2
from customer c
inner join purchases p on (c.customerid = p.customerid)
order by c.customerid,
p.productid1,
p.productid2;
CUSTOMERID NAME PRODUCTID1 PRODUCTID2
---------- ---- ---------- ----------
1 bob 51
1 bob 52
1 bob 81
1 bob 82
1 bob 83
2 ted 52

It's probably easiest to just change it to a union query like this.
select customer.customerid, purchase1.productid as P1, null as P2
from customer
left join purchase1
on customer.customerid = purchase1.customerid
union all
select customer.customerid, null as P1, purchase2.productid as P2
from customer
left join purchase2
on customer.customerid = purchase2.customerid
where customer.customerid = 1;

This uses Union, but in a slightly different way, within subqueries, which might provide you more flexibility.
select distinct t1.pID,t2.pID
from (select ID,pID from Puchase1
union all
select ID, null from Purchase1) t1
right join (select ID,pID from Purchase2
union all
select ID, null from Purchase2) t2
on t1.ID = t2.ID
where t1.ID = 1
and (t1.pID is not null or t2.pID is not null)
and (t1.pID is null or t2.pID is null)

Related

Sql - Getting Sum on the join table

I just want to get the result which displays the reference which is not tallied in sum of table2. when i run my query below it will give me an wrong sum which it gets doubled even if group by cusid ,refno.
Table 1
RefNo
CusID
TotalAmount
1
1001
50
2
1001
30
3
1002
40
Table 2
RefNo
CusID
Particular
Amount
1
1001
Paper
30
1
1001
Pencil
30
2
1001
Ball
15
2
1001
Rubber
20
3
1002
Laptop
50
select * from Table1 a
INNER JOIN (Select CusID,RefNo, SUM(Amount) as CorrectTotal from Table2 group by
CusID,RefNo,
)b
ON b.CusID= a.CusID AND b.RefNo= a.RefNo
where a.TotalAmount != CorrectTotal
Expected Result
If you do it with FULL JOIN and with GROUP BY, you will also get rows where there is no record in the other table.
SELECT COALESCE(a.RefNo, b.RefNo) AS RefNo
, COALESCE(a.CusID, b.CusID) AS CusID
, a.TotalAmount
, SUM(b.Amount) AS CorrectTotal
FROM table1 a
FULL JOIN table2 b ON a.RefNo = b.RefNo
AND a.CusID = b.CusID
GROUP BY COALESCE(a.RefNo, b.RefNo)
, COALESCE(a.CusID, b.CusID)
, a.TotalAmount
ORDER BY 1, 2
Output
RefNo
CusID
TotalAmount
CorrectTotal
1
1001
50
60
2
1001
30
35
3
1002
40
50
8
888
88
(null)
9
999
(null)
99
See running demo on SQL Fiddle.
The other answer will work, but if you don't want to mess with a GROUP BY on the whole query you can also use an APPLY to do this:
SELECT a.*, c.CorrectAmount
FROM Table1 a
OUTER APPLY (
SELECT SUM(Amount) AS CorrectAmount
FROM Table2 b
WHERE b.CusID = a.CusID AND b.RefNo = a.RefNo
) c
WHERE a.TotalAmount <> c.CorrectAmount

Average by rows with sqlite

I'm working with a sqlite database.
The tables are:
ID_TABLE POINTS_A_TABLE POINTS_B_TABLE
id number id_a points_a id_a points_a
-------------- ---------------- ----------------
smith 1 smith 11 ...
gordon 22 gordon 11
butch 3 butch 11
sparrow 25 sparrow
white 76 white 46
and so on. After these commands
select id,
points_a_table.points_a, points_b_table.points_a, points_c_table.points_a, points_d_table.points_a
from id_table
left join points_a_table on points_a_table.id_a = id_table.id
left join points_b_table on points_b_table.id_a = id_table.id
left join points_c_table on points_c_table.id_a = id_table.id
left join points_d_table on points_d_table.id_a = id_table.id
group by id
I got this result, on each row I have the id and the points associated with the id.
Now I'd like to get an average of points by row, sorted by average in descending order.
What I want is:
sparrow| 56 [(44+68)/2]
white | 41 ([46+67+11)/3]
smith | 33 [(11+25+65)/3]
butch | 24 [(11+26+11)/3]
gordon | 11 [11/1]
How can I do that?
Thanks.
If you smush together all point tables, you can then simply compute the average for each group:
SELECT id,
avg(points_a)
FROM (SELECT id_a AS id, points_a FROM points_a_table
UNION ALL
SELECT id_a AS id, points_a FROM points_b_table
UNION ALL
SELECT id_a AS id, points_a FROM points_c_table
UNION ALL
SELECT id_a AS id, points_a FROM points_d_table)
GROUP BY id
ORDER BY avg(points_a) DESC;

How to make joins in my criteria in SQL SERVER?

I have three tables:
**1.FT_ViewItemMovement **
StoreID ItemLookupCode QTY
-------------------------------
1001 11121111 222
1001 11121111 1
1201 11121111 333
1201 11121111 2
2.Item
ItemLookupCode ID
------------------------
11121111 111
3.ItemDynamic
ItemID StoreID SnapShotQuantity
-------------------------------------
111 1201 50
111 1001 25
111 5000 75
111 7777 100
Expecting Result
ItemID StoreID QTY SnapShotQuantity
-------------------------------------
111 1201 335 50
111 1001 223 25
111 5000 0 75
111 7777 0 100
I Tried this following Query, But I didn't get
SELECT
ViewItemMovement.ItemLookupCode,
ViewItemMovement.StoreID,
Sum(ViewItemMovement.Quantity) Quantity,
ItemDynamic.SnapShotQuantity SanpShotQuantity
FROM
FT_ViewItemMovement ViewItemMovement
left join Item with(NoLock) on Item.ItemLookupCode = ViewItemMovement.ItemLookupCode
left join ItemDynamic with(NoLock) on ItemDynamic.ItemID = Item.ID and ItemDynamic.StoreID = ViewItemMovement.StoreID
WHERE brand = 'PEPSI'
Group By
ViewItemMovement.ItemLookupCode,
ViewItemMovement.StoreID,
ViewItemMovement.ItemDescription,
ViewItemMovement.Brand,
Item.Cost,
ItemDynamic.SnapShotQuantity
first I want to summarise the quanity of FT_ViewItemMovement.Qty by StoreID and ItemLookupCode and then if it matches the same itemID with ItemDynamic it has to bring SnapShotQuanity
If there is no FT_ViewItemMovement.Qty, then show FT_ViewItemMovement.Qty = 0 as Expecting output
Simplify your query by looking at what you need, which is two SUM's and a GROUP BY containing the other selections, while using StoreID from FT_ViewItemMovement to allow the SUM(v.QTY) to work.
SELECT id.ItemID, v.StoreID, SUM(v.QTY), SUM(id.SnapShotQuantity)
FROM FT_ViewItemMovement v
INNER JOIN Item i ON v.ItemLookupCode = i.ItemLookupCode
INNER JOIN ItemDynamic id ON i.ID = id.ItemID
GROUP BY id.ItemID, v.StoreID
Your GROUP BY clause looks too complicated. I think this is what you want:
SELECT vim.ItemLookupCode, vim.StoreID,
COALESCE(Sum(vim.Quantity), 0) as Quantity,
id.SnapShotQuantity as SanpShotQuantity
FROM FT_ViewItemMovement vim LEFT JOIN
Item i
ON i.ItemLookupCode = vim.ItemLookupCode LEFT JOIN
ItemDynamic id
ON id.ItemID = i.ID and id.StoreID = vim.StoreID
WHERE vim.brand = 'PEPSI'
GROUP BY vim.ItemLookupCode, vim.StoreID,
id.SnapShotQuantity;
Your WHERE clause references brand. I have no idea where that comes from based on your question.
With your data sample, you must select from ItemDynamic then use join other table.
I hope it will work for you.
SELECT
ItemDynamic.ItemID,
ItemDynamic.StoreID,
SUM(ViewItemMovement.Quantity) AS Quantity,
MAX(ItemDynamic.SnapShotQuantity) AS SanpShotQuantity
FROM
ItemDynamic with(NoLock)
left join Item with(NoLock) on Item.ID = ItemDynamic.ItemID
left join FT_ViewItemMovement ViewItemMovement on ViewItemMovement.ItemLookupCode = Item.ItemLookupCode and ViewItemMovement.StoreID = ItemDynamic.StoreID
Group By
ItemDynamic.ItemID,
ItemDynamic.StoreID

How to use case after where clause in sql server

category Item Price
A Pen NULL
B Pen 10
A Pencil 10
B Pencil 8
C Pencil 7
A Note Book 40
B Note Book 30
C Note Book 20
A Bottle NULL
B Bottle 80
A Ball 50
B Ball 40
A Bag 1000
B Bag 800
This is My data i want to Show only category A data if A price is null
then show category B price. I was tried but don't know how i show the
Data
select * from tbl1
where category = case when price is null then 'B' else 'A' end
When Run this query it's show only Category A data
category Item Price
A Pencil 10
A Note Book 40
A Ball 50
A Bag 1000
Something like this
SELECT price
FROM table
WHERE
category = CASE
WHEN price is not null THEN 'A'
ELSE 'B'
END
`
Using a left join and coalesce() (you could also use isnull()):
select a.Category, a.Item, coalesce(a.Price,b.Price) as Price
from yourtable a
left join yourtable b
on a.Item = b.Item
and b.category = 'B'
where a.category = 'A'
coalesce() will return the first non null value from the parameters in order from left to right.
rextester demo: http://rextester.com/FZO89906
returns:
+----------+-----------+-------+
| Category | Item | Price |
+----------+-----------+-------+
| A | Pen | 10 |
| A | Pencil | 10 |
| A | Note Book | 40 |
| A | Bottle | 80 |
| A | Ball | 50 |
| A | Bag | 1000 |
+----------+-----------+-------+
Reference:
coalesce()
isnull()
;WITH cte(category,Item,Price)
AS
(
SELECT 'A','Pen' ,NULL UNION ALL
SELECT 'B','Pen' ,10 UNION ALL
SELECT 'A','Pencil' ,10 UNION ALL
SELECT 'B','Pencil' ,8 UNION ALL
SELECT 'C','Pencil' ,7 UNION ALL
SELECT 'A','Note Book' ,40 UNION ALL
SELECT 'B','Note Book' ,30 UNION ALL
SELECT 'C','Note Book' ,20 UNION ALL
SELECT 'A','Bottle' ,NULL UNION ALL
SELECT 'B','Bottle' ,80 UNION ALL
SELECT 'A','Ball' ,50 UNION ALL
SELECT 'B','Ball' ,40 UNION ALL
SELECT 'A','Bag' ,1000 UNION ALL
SELECT 'B','Bag' ,800
)
SELECT category,Item,Price From
(
SELECT *,ROW_NUMBER()Over(Partition by Price order by Price)seq From
(
SELECT o.* FROM cte i
INNER JOIN cte o
ON o.category=i.category
WHERE o.category='A' AND o.Price!=i.Price
)dt
) Final
where Final.seq=1
OutPut
category Item Price
A Pencil 10
A Note Book 40
A Ball 50
A Bag 1000
Output For value'B'
category Item Price
--------------------------------
B Pencil 8
B Pen 10
B Note Book 30
B Ball 40
B Bottle 80
B Bag 800
I understand it as If an item in a category has a null price it should pick up that specific item from another category having a price
--Filter records having prices
with CTETable as
(
Select distinct category, item, price
from tbl1
where price is not null
)
--distinct items
select * from
(select category, item, price, ROW_NUMBER() over (Partition by Item order by
Category) as RowNo from CTETable)
as c
where c.RowNo=1

SQL - Find the Top Level Parent and Multiply Quantities

I have two tables which track part numbers as well as the hierarchy of assemblies.
Table: Config
ConfigNum AssemblyNum Qty
1 A 1
1 B 2
1 C 2
2 A 1
2 C 1
Table: SubAssembly
SubAssembly PartNum Qty
A AA 2
A BB 4
A CC 2
A DD 4
B EE 4
B FF 8
AA AAA 2
I would like to create a flat version of these tables which shows the ConfigNum (Top level parent) with all associated assembly and part numbers, for each ConfigNum in the Config table. The column Config.AssemblyNum is equivalent to SubAssembly.SubAssembly.
The Subassembly table shows the partent to child relation ship between parts. For example Assembly 'A' has a child assembly 'AA'. Since 'AA' exists in the SubAssembly Column is it self an assembly and as you can see it has a child part 'AAA'. 'AAA' does not exist in the SubAssemly columns therefore it is the last child in the series.
I would also like to have an accurate quantity count of each part based on multiplying of parent to child down the chain.
For example in the output:
(Total Qty of AAA) = (Qty A) x (Qty AA) x (Qty AAA)
4 = 1 x 2 x 2
Example Output table: (for Config 1)
ConfigNum SubAssembly PartNum TotalQty
1 A AA 2
1 A BB 4
1 A CC 2
1 A DD 4
1 B EE 8
1 B FF 16
1 A AAA 4
Any suggestion on how to complete this task would be greatly appreciated.
EDIT: I have been able to create this code based on suggestions made in the answers.
I am still having trouble getting the quantities to multiply down.
I have received the error "Types don't match between the anchor and the recursive part in column "PartQty" of recursive query "RCTE"."
;WITH RCTE (AssemblyNum, PartNum, PartQty, Lvl) AS
(
SELECT AssemblyNum, PartNum, PartQty, 1 AS Lvl
FROM SP_SubAssembly r1
WHERE EXISTS (SELECT * FROM SP_SubAssembly r2 WHERE r1.AssemblyNum = r2.PartNum)
UNION ALL
SELECT rh.AssemblyNum, rc.PartNum, (rc.PartQty * rh.PartQty), Lvl+1 AS Lvl
FROM dbo.SP_SubAssembly rh
INNER JOIN RCTE rc ON rh.PartNum = rc.AssemblyNum
)
SELECT CB.ID, CB.ConfigNum, CB.PartNum, CB.PartQty, r.AssemblyNum, r.PartNum, SUM(r.PartQty * COALESCE(CB.PartQty,1)) AS TotalQty
FROM SP_ConfigBOM CB
FULL OUTER JOIN RCTE r ON CB.PartNum = r.AssemblyNum
WHERE CB.ConfigNum IS NOT NULL
ORDER BY CB.ConfigNum
Thanks,
For this problem I think you must use a recursive query. In fact I think SubAssembly table should have some ProductID field other than SubAssembly to easily identify the main product that contains assemblies.
You can find a similar example in SLQ Server documentation.
Can check it here: http://rextester.com/FQYI80157
Change the Qty in Config table to change the final result.
create temp table t1 (cfg int, part varchar(10), qty int);
create temp table t2 (part varchar(10), sasm varchar(10), qty int);
insert into t1 values (1,'A',2);
insert into t2 values ('A','AA',2);
insert into t2 values ('A','BB',4);
insert into t2 values ('A','CC',2);
insert into t2 values ('A','DD',4);
insert into t2 values ('AA','AAA',2);
WITH cte(sasm, part, qty)
AS (
SELECT sasm, part, qty
FROM #t2 WHERE part = 'A'
UNION ALL
SELECT p.sasm, p.part, p.qty * pr.qty
FROM cte pr, #t2 p
WHERE p.part = pr.sasm
)
SELECT #t1.cfg, cte.part, cte.sasm, SUM(cte.qty * COALESCE(#t1.qty,1)) as total_quantity
FROM cte
left join #t1 on cte.part = #t1.part
GROUP BY #t1.cfg, cte.part, cte.sasm;
This is the result:
+------+------+----------------+
| part | sasm | total_quantity |
+------+------+----------------+
| A | AA | 4 |
+------+------+----------------+
| A | DD | 8 |
+------+------+----------------+
| AA | AAA | 4 |
+------+------+----------------+
| A | BB | 8 |
+------+------+----------------+
| A | CC | 4 |
+------+------+----------------+
insert into #Temp
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
B.[PartNum],
A.[Qty],
A.QTY * B.Qty TotalQty
INTO #Temp
FROM [Config] A,
[SubAssembly] B
WHERE A.[AssemblyNum] = B.[SubAssembly]
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
A.[PartNum],
A.[Qty],
A.TotalQty
FROM #Temp A
union
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
B.[PartNum],
A.[Qty],
A.TotalQty * B.Qty
FROM #Temp A,
[SubAssembly] B
WHERE
A.[PartNum] = B.[SubAssembly]