sql Left join giving duplicate multiple values - sql

Friends I am unable to fetch correct result Please suggest , Thanks in Advance .
I have two tables and trying to get balance quantity
One is Purchase Table (purchase_detail)
Pur_Date Item_Id Pur_Qty
2014-10-08 12792 25
2014-11-01 133263 20
2014-10-01 133263 2
2014-11-20 12792 10
Second is Sale Table (sale_detail)
Sale_Date Item_Id Sale_Qty
2014-11-17 133263 -6
2014-11-05 12792 -1
2014-11-24 133263 -2
2014-10-28 12792 -6
2014-11-05 133263 -2
After using left join
SQL :
select a.pur_item, sum(a.pur_qty + b.sold_qty ) as bal_qty
from purchase_item_qty_amount a left join sale_item_qty_amount b
on a.pur_item =b.sale_item where a.pur_item IN( 12792,133263)
group by 1;
Result - But it's incorrect
Item_Id Bal_qty
12792 56
133263 46
Result - It should be
Item_Id Bal_qty
12792 28
133263 12

Try this untested query:
select pur_item, sum(as bal_qty) from (
select pur_item, sum(a.pur_qty) as bal_qty
from purchase_item_qty_amount a
group by 1
union
select pur_item, sum(b.sold_qty)as bal_qty
from sale_item_qty_amount b
group by 1)
group by 1

You have to use union instead of join as the left rows have multiple matches to the right which cause too much data to be calculated. Try running your query without the sum and group by to see the raw data being calculated and you will see what I mean.
Your query with no group/sum:
select a.pur_item, a.pur_qty, b.sold_qty from purchase_item_qty_amount a left join sale_item_qty_amount b on a.pur_item = b.sale_item where a.pur_item IN (12792,133263);
Here is a query that works as you would expect:
select item, sum(a.pur_qty) + sum(a.sold_qty) as bal_qty from (select pur_item as item, sum(pur_qty) as pur_qty, 0 as sold_qty from purchase_item_qty_amount where pur_item in (12792,133263) group by pur_item union select sale_item as item, 0 as pur_qty, sum(sold_qty) as sold_qty from sale_item_qty_amount where sale_item in (12792,133263) group by sale_item) a group by item;

Here is the full dummy of your question,try this one you will get your desired result:-
purchase table
create table pt(Pur_Date date,Item_Id number, Pur_Qty number)
insert into pt values('08.oct.2014',12792,25);
insert into pt values('01.nov.2014',133263,20);
insert into pt values('01.oct.2014',133263,2);
insert into pt values('20.nov.2014',12792,10);
sales table
create table st(Sale_Date date , Item_Id number, Sale_Qty number)
insert into st values('17.nov.2014',133263,-6);
insert into st values('05.nov.2014',12792,-1);
insert into st values('24.nov.2014',133263,-2);
insert into st values('28.oct.2014',12792,-6);
insert into st values('05.nov.2014',133263,-2);
select purchase.Item_Id,(purchase.pur_qty+sale.sale_qty) bal_qty
from
(
select a.Item_Id, sum(a.pur_qty) pur_qty
from pt a
group by a.Item_Id
) purchase
left join
(
select b.Item_Id,
sum(b.sale_qty ) sale_qty
from st b
group by b.Item_Id
) sale
on sale.Item_Id = purchase.Item_Id
where purchase.Item_Id in (12792,133263)
order by 1;

Related

BigQuery: Subquery with UNION as ARRAY

I have the following two example tables
Orders Table
order_id
linked_order1
linked_order2
1001
L005
null
1002
null
null
1003
L006
L007
Invoices Table
order_id
linked_order_id
charge
1001
null
4.27
1002
null
9.82
1003
null
7.42
null
L005
2.12
null
L006
1.76
null
L007
3.20
I need to join these so the charges of all the orders (linked and otherwise) can be shown as part of the single order row. My desired output is something like this.
Desired Output
order_id
linked_order1
linked_order2
invoices.charge
invoices.order_id
invoices.linked_order_id
1001
L005
null
4.27
1001
null
2.12
null
L005
1002
null
null
9.82
null
null
1003
L006
L007
7.42
null
null
1.76
null
L006
3.20
null
L007
I can manage to get the main order into the table as follows.
SELECT
orders,
ARRAY(
SELECT AS STRUCT * FROM `invoices_table` WHERE order=orders.order_id) AS invoice
FROM
`orders_table` AS orders
I can run a separate query to union all of the invoice results into a single table for given order ids but I can't combine this with the above query with out getting errors.
Something like this...
SELECT
orders,
ARRAY(
SELECT AS STRUCT * FROM
(SELECT * FROM `invoices_table` WHERE order=orders.order_id
UNION ALL SELECT * FROM `invoices_table` WHERE linked_order_id=orders.linked_order1
UNION ALL SELECT * FROM `invoices_table` WHERE linked_order_id=orders.linked_order2)
) AS invoice
FROM
`orders_table` AS orders
But this gives me the correlated subqueries error.
[Update]
This is much simpler than I thought. The following query gives me what I was after.
SELECT
orders,
ARRAY(
SELECT AS STRUCT * FROM `invoices_table` WHERE order=orders.order_id OR linked_order_id IN(orders.linked_order1, orders.linked_order2)) AS invoice
FROM
`orders_table` AS orders
Using CROSS JOINS,
SELECT o.*, ARRAY_AGG(i) invoices
FROM Orders o, Invoices i
WHERE o.order_id = i.order_id
OR i.linked_order_id IN (o.linked_order1, o.linked_order2)
GROUP BY 1, 2, 3;
Query results
[UPDATE]
Sometimes the query using OR conditions in WHERE clause might show poor perfomrance in large dataset. In that case you may try below query instead that generates same result.
SELECT o.*, ARRAY_AGG(i) invoices FROM (
SELECT o, i FROM Orders o JOIN Invoices i USING (order_id)
UNION ALL
SELECT o, i FROM Orders o JOIN Invoices i ON i.linked_order_id IN (o.linked_order1, o.linked_order2)
) GROUP BY 1, 2, 3;
For the desired output table, the full outer join is the right command.
with tblA as (Select order_id, 1 linked_order1, 2 linked_order2, from unnest([1,2,3]) order_id),
tblB as (Select order_id, 109.99 charge from unnest([3,4,5]) order_id
union all select null order_id, * from unnest([50.1,29.99]) charge
)
Select *
from tblA
full join tblB
using(order_id)
For your setting, there is the need to have several joining conditions. Therefore, the first table is used three times, for each joining key.
with tblA as (Select order_id, "L05" linked_order1, "L2" linked_order2, from unnest(["1","2","3"]) order_id),
tblB as (Select order_id, null linked_order_id, 109.99 charge from unnest(["3","4","5"]) order_id
union all select null order_id, "L05" , * from unnest([50.1,29.99]) charge
)
Select A.order_id,linked_order1,linked_order2, array_agg(struct(tblB.order_id,linked_order_id,charge))
from
(
Select * from tblA, unnest([order_id,linked_order1,linked_order2]) as tmp_id
) A
full join tblB
on tmp_id = ifnull(tblB.order_id,linked_order_id)
where charge is not null #or tmp_id=A.order_id
group by 1,2,3

ORACLE SQL - multiple JOINs from same table

I have data related material transactions in one table and log history header data related to materials is in another table and detailed log history data in third table. I'm trying to get different status update dates matched to material table but I get duplicate rows for one material transaction
Original material transaction table:
ORDER_NO
MATERIAL
QTY
0001
MAT01
2
0002
MAT02  
5
Original Log History Header transaction table:
ORDER_NO
LOG_ID
0001
1001
0001
1002
Status code 1 refers to Opened and code 2 to Closed
Detailed Log History table:
LOG_ID
STATUS_CODE
DATE
1001
1
11/12/2021
1002
2  
15/12/2021
With following SQL query:
SELECT
TO_CHAR (m.order_no) order_no,
m.material,
a.date opened_date,
ab.closed_date
FROM MATERIAL_TRANSACTIONS m
INNER JOIN HISTORY_LOG t
ON m.ORDER_NO = t.ORDER_NO
INNER JOIN HISTORY_LOG_DETAILED a
ON t.LOG_ID = a.LOG_ID
AND a.STATUS_CODE = '1'
INNER JOIN HISTORY_LOG_DETAILED ab
ON t.LOG_ID = ab.LOG_ID
AND ab.STATUS_CODE = '2'
I get following result:
ORDER_NO
MATERIAL
QTY
OPENED_DATE
CLOSED_DATE
0001
MAT01
2
11/12/2021
0001
MAT01  
2
15/12/2021
And I would like to get the status dates to the same row as below:
ORDER_NO
MATERIAL
QTY
OPENED_DATE
CLOSED_DATE
0001
MAT01
2
11/12/2021
15/12/2021
I would appreciate all the help I can get and am very sorry if there already is topic for similar issue.
Your problem occurs because you join the history table, which holds 2 records for the order. You could flatten this if you use 2 inline tables that hold exactly 1 record.
with opened_dates as (
select h.order_id, d.date
from history h
inner join details d on h.log_id = d.log_id and d.status_code = '1'
), closed_dates as (
select h.order_id, d.date
from history h
inner join details d on h.log_id = d.log_id and d.status_code = '2'
)
select to_char (m.order_no) order_no,
m.material,
o.date opened_date,
c.date closed_date
from material_transactions m
join opened_dates o on m.order_no = o.order_no
join closed_dates c on m.order_no = c.order_no
;
Just an idea :
I joined HISTORY_LOG and HISTORY_LOG_DETAILED tables to get dates for specific status, and set as OPENED_DATE and CLOSED_DATE (if status 1 , then opened date is DATE column, otherwise set it as 01.01.0001)
After that grouped those records by ORDER_NO and maxed the date values to get actual OPENED_DATE and CLOSED_DATE .
Finally joined this subquery with MATERIAL_TRANSACTIONS table :
SELECT
TO_CHAR (M.ORDER_NO) ORDER_NO,
M.MATERIAL,
QTY,
L_T.OPENED_DATE,
L_T.CLOSED_DATE
FROM MATERIAL_TRANSACTIONS M
INNER JOIN
(
SELECT L.ORDER_NO ,
MAX( CASE WHEN LD.STATUS_CODE = 1 THEN LD.DATE ELSE TO_DATE('01.01.0001','dd.mm.yyyy') END ) OPENED_DATE
MAX( CASE WHEN LD.STATUS_CODE = 2 THEN LD.DATE ELSE TO_DATE('01.01.0001','dd.mm.yyyy') END ) CLOSED_DATE
FROM
HISTORY_LOG L
INNER JOIN HISTORY_LOG_DETAILED LD ON LD.LOG_ID = L.LOG_ID
GROUP BY L.ORDER_NO
) L_T on L_T.ORDER_NO = M.ORDER_NO
Note: I didnt test it. So there can be small syntax errors. Please check it and for better help add a fiddle so i can test my query

How to compare a column from a table to a text in a column in another table

I have two tables in my DB
Table1:
caseId name ApprovalDate ApprovalSatus
150150 John 2018-01-29 41
150151 Dave 2018-01-02 41
Table2:
contId StatusSourceNoKeyId Status contractDate
210210 38^|^150150^|^31796794 41 2018-02-01
210211 38^|^150150^|^31796797 40 2018-01-28
210220 38^|^150151^|^31796720 41 2018-01-03
I need to find out date difference between contractDate in Table2 and ApprovalDate in Table1 when CaseId in Table1 is like
'38^|^caseId^|^31796794' in table2 and Status is '41'.
How can I do such a select?
Thanks,
In SQL-Server you have the DATEDIFF Function
Quick Example:
select
DATEDIF(day, b.contractDate, a.ApprovalDate) as difference, *
from Table1 a
left join
Table2 b on b.StatusSourceNoKeyId like '%^'+ a.caseId +'^%'
and a.ApprovalStatus = b.Status
this should work, i haven't tested it though.
For convenience, I have changed the query to a Common Table Expression. You can use your actual tables for this.
;WITH CTE1
AS
(
SELECT '150150' caseId, 'John' Name, '2018-01-29' ApprovalDate, 41 'ApprovalSatus' UNION
SELECT '150151' ,'Dave' ,'2018-01-02' , 41
)
, CTE2
AS
(
SELECT 210210 contId, '38^|^150150^|^31796794' StatusSourceNoKeyId, 41 Status, '2018-02-01' contractDate UNION
SELECT 210211 ,'38^|^150150^|^31796797', 40 ,'2018-01-28' UNION
SELECT 210211 ,'38^|^150152^|^31796797', 40 ,'2018-01-28' UNION
SELECT 210220 ,'38^|^150151^|^31796720', 41 ,'2018-01-03'
)
SELECT CTE1.caseId, DATEDIFF(DD, cte1.ApprovalDate, cte2.contractDate) DatDiff FROM CTE1 INNER JOIN CTE2 ON
CTE2.StatusSourceNoKeyId LIKE '%^'+CTE1.caseId+'^%'
WHERE CTE2.Status = '41'
In your case the query could be like
SELECT Tablle1.caseId, DATEDIFF(DD, Tablle1.ApprovalDate, Tablle2.contractDate) DatDiff FROM Tablle1 INNER JOIN Tablle2 ON
Tablle2.StatusSourceNoKeyId LIKE '%^'+Tablle1.caseId+'^%'
WHERE Tablle2.Status = '41'

How to query SQLite to find sum, latest record, grouped by id, in between dates?

Items (itemId, itemName)
Logs (logId, itemId, qtyAdded, qtyRemoved, availableStock, transactionDate)
Sample Data for Items:
itemId itemName
1 item 1
2 item 2
Sample Data for Logs:
logid itemId qtyAdded qtyRemoved avlStock transDateTime
1 2 5405 0 5405 June 1 (4PM)
2 2 1000 0 6405 June 2 (5PM)
3 2 0 6000 405 June 3 (11PM)
I need to see all items from Items table and their SUM(qtyAdded), SUM(qtyRemoved), latest availableStock (there's an option for choosing the range of transactionDate but default gets all records). Order of date in final result does not matter.
Preferred result: (without date range)
itemName qtyAddedSum qtyRemovedSum avlStock
item 1 6405 6000 405
item 2 <nothing here yet>
With date Range between June 2 (8AM) and June 3 (11:01PM)
itemName qtyAddedSum qtyRemovedSum avlStock
item 1 1000 6000 405
item 2 <no transaction yet>
So as you can see, final result is grouped which makes almost all my previous query correct except my availableStock is always wrong. If I focus in the availableStock, I can't get the two sums.
you could use group by sum, and between
select itemName, sum(qtyAdded), sum(qtyRemoved), sum(avlStock)
from Items
left join Logs on logs.itemId = items.itemId
where transDateTime between '2017-06-02 08:00:00' and '2017-06-03 23:00:00'
group by itemId
or
If you need the last avlStock
select itemName, sum(qtyAdded), sum(qtyRemoved), tt.avlStock
from Items
left join Logs on logs.itemId = items.itemId
INNER JOIN (
select logid,avlStock
from logs
inner join (
select itemId, max(transDateTime) max_trans
from Logs
group by itemId
) t1 on logs.itemId = t1.ItemId and logs.transDateTime = t1.max_trans
) tt on tt.logId = Logs.itemId
where transDateTime between '2017-06-02 08:00:00' and '2017-06-03 23:00:00'
group by itemId
Okay, I tried both of these and they worked, can anyone confirm if these are already efficient or if there are some more efficient answers there.
SELECT * FROM Items LEFT JOIN
(
SELECT * FROM Logs LEFT JOIN
(
SELECT SUM(qtyAdd) AS QtyAdded, SUM(qtySub) AS QtyRemoved, availableStock AS Stock
FROM Logs WHERE transactionDate BETWEEN julianday('2017-07-18 21:10:40')
AND julianday('2017-07-18 21:12:00') GROUP BY itemId
)
ORDER BY transactionDate DESC
)
USING (itemId) GROUP BY itemName;
SELECT * FROM Items LEFT JOIN
(
SELECT * FROM Logs LEFT JOIN
(
SELECT SUM(qtyAdd) AS QtyAdded, SUM(qtySub) AS QtyRemoved, availableStock AS Stock
FROM Logs GROUP BY itemId
)
ORDER BY transactionDate DESC
)
USING (itemId) GROUP BY itemName;

SQL Server 2008 - need help on a antithetical query

I want to find out meter reading for given transaction day. In some cases there won’t be any meter reading and would like to see a meter reading for previous day.
Sample data set follows. I am using SQL Server 2008
declare #meter table (UnitID int, reading_Date date,reading int)
declare #Transactions table (Transactions_ID int,UnitID int,Transactions_date date)
insert into #meter (UnitID,reading_Date,reading ) values
(1,'1/1/2014',1000),
(1,'2/1/2014',1010),
(1,'3/1/2014',1020),
(2,'1/1/2014',1001),
(3,'1/1/2014',1002);
insert into #Transactions(Transactions_ID,UnitID,Transactions_date) values
(1,1,'1/1/2014'),
(2,1,'2/1/2014'),
(3,1,'3/1/2014'),
(4,1,'4/1/2014'),
(5,2,'1/1/2014'),
(6,2,'3/1/2014'),
(7,3,'4/1/2014');
select * from #meter;
select * from #Transactions;
I expect to get following output
Transactions
Transactions_ID UnitID Transactions_date reading
1 1 1/1/2014 1000
2 1 2/1/2014 1010
3 1 3/1/2014 1020
4 1 4/1/2014 1020
5 2 1/1/2014 1001
6 2 3/1/2014 1001
7 3 4/1/2014 1002
Your SQL Query to get your desired out put will as following:
SELECT Transactions_ID, T.UnitID, Transactions_date
, (CASE WHEN ISNULL(M.reading,'') = '' THEN
(
SELECT MAX(Reading) FROM #meter AS A
JOIN #Transactions AS B ON A.UnitID=B.UnitID AND A.UnitID=T.UnitID
)
ELSE M.reading END) AS Reading
FROM #meter AS M
RIGHT OUTER JOIN #Transactions AS T ON T.UnitID=M.UnitID
AND T.Transactions_date=M.reading_Date
I can think of two ways to approach this - neither of them are ideal.
The first (and slightly better) way would be to create a SQL Function that took the Transactions_date as a parameter and returned the reading for Max(Reading_date) where reading_date <= transactions_date. You could then use this function in a select statement against the Transactions table.
The other approach would be to use a cursor to iterate through the transactions table and use the same logic as above where you return the reading for Max(Reading_date) where reading_date <= transactions_date.
Try the below query:
Please find the result of the same in SQLFiddle
select a.Transactions_ID, a.UnitID, a.Transactions_date,
case when b.reading IS NULL then c.rd else b.reading end as reading
from
Transactions a
left outer join
meter b
on a.UnitID = b.UnitID
and a.Transactions_date = b.reading_Date
inner join
(
select UnitID,max(reading) as rd
from meter
group by UnitID
) as C
on a.UnitID = c.UnitID