How to change query result without using pivot functions? - sql

I have a query which shows number of cheap and expensive orders for a date:
SELECT od1.date,
(SELECT COUNT( od2.id) FROM myorders od2
INNER JOIN myproducts ON od2.product_id = myproducts.id
WHERE od2.date = od1.date
AND myproducts.price BETWEEN 500 AND 2000
) cheap_orders,
(SELECT SUM(myproducts.price)
FROM myorders od2
INNER JOIN myproducts ON od2.product_id = myproducts.id
WHERE od2.date = od1.date
AND myproducts.price BETWEEN 500 AND 2000
) sum_ch_o,
(SELECT COUNT( od2.id) FROM myorders od2
INNER JOIN myproducts ON od2.product_id = myproducts.id
WHERE od2.date = od1.date
AND myproducts.price > 2000
) expensive_orders,
(SELECT SUM(myproducts.price)
FROM myorders od2
INNER JOIN myproducts ON od2.product_id = myproducts.id
WHERE od2.date = od1.date
AND myproducts.price > 2000
) sum_exp_o
FROM myorders od1
GROUP BY od1.date
It returns me a table:
date cheap_orders sum_ch_o expensive_orders sum_exp_o
2018-06-06 0 0 1 4000
2018-06-04 2 3100 0 0
2018-06-07 1 780 3 28000
2018-06-05 1 560 4 15500
How can I modify my query to get another view of this table:
date order_cat quantity sum
2018-06-06 cheap 0 0
2018-06-06 expensive 1 4000
2018-06-04 cheap 2 3100
2018-06-04 expensive 0 0
2018-06-07 cheap 1 780
2018-06-07 expensive 3 28000

Related

Opening Stock, Closing Stock By Date sql Query

I have two table with primary key and foreign key (MaterialId)
Material Table (Multiple Material)
MaterialId MaterialName OpeningStock
1 Pen 100
2 Pencil 50
Material Stock (Multiple Material Entry)
MaterialId PurchaseQty SalesQty Date
1 500 0 2016-12-15
1 0 0 2016-12-16
1 300 0 2016-12-17
1 0 400 2016-12-18
1 0 0 2016-12-19
1 0 0 2016-12-20
1 0 400 2016-12-21
1 200 100 2016-12-22
Now When I Pass #FromDate and #Todate
I want to output like below:
Date MaterialName OpeningStock PurchaseQty SalesQty ClosingStock
2016-12-15 Pen 100 500 0 600
2016-12-16 Pen 600 0 0 600
2016-12-17 Pen 600 300 0 900
2016-12-18 Pen 900 0 400 500
2016-12-19 Pen 500 0 0 500
2016-12-20 Pen 500 0 0 500
2016-12-21 Pen 500 0 400 100
2016-12-22 Pen 100 200 100 200
Note :
1. If Something is wrong on database tables so, please guide me how to handle this situation.
2. And Also find Current Date Stock From Two Tables
You are looking for a rolling sum of the various quantity values. One way to do this is using correlated subqueries:
SELECT
t1.Date,
mt.MaterialName,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date < t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS OpeningStock,
t1.PurchaseQty,
t1.SalesQty,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date <= t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS ClosingStock
FROM [Material Stock] t1
INNER JOIN [Material Table] mt
ON t1.MaterialId = mt.MaterialId
ORDER BY
mt.MaterialName,
t1.Date;
Note that it is bad table design to be storing the opening stock values in a separate table from the material stock table. This means the above query would return no pencil records. A better approach would be to insert a seed record into material stock, for each material, with the amount being the initial stock.
Output:
Demo here:
Rextester
Simply do as below :
SELECT S.DATE, M.MaterialName, M.OpeningStock, S.PurchaseQty, S.SalesQty, SUM((M.OpeningStock+S.PurchaseQty)-S.SalesQty)ClosingStock FROM #TABLE
(
SELECT * FROM MaterialTABLE
) M
INNER JOIN Material S ON S.MaterialId = M.MaterialId where s.date between #FromDate and #Todate

sum result is being multiplied by number of rows in oracle

I have a view named b_balance which returns the following records:
SECURITIES_CODE BUY_SELL_FLAG C_BALANCE P_BALANCE
--------------- ------------- --------- ---------
10042 BUY 200 0
10042 BUY 500 0
10042 SELL 200 0
10042 BUY 0 5000
10042 SELL 0 2500
10043 BUY 300 0
10043 SELL 0 2500
and another view named as t_balance which returns the following records:
SECURITIES_CODE BUY_SELL_FLAG C_BALANCE P_BALANCE
--------------- ------------- --------- ---------
10042 BUY 0 5000
10043 BUY 300 0
10042 SELL 200 0
10042 SELL 0 2500
10043 SELL 0 2500
10042 BUY 200 0
10042 BUY 500 0
Now the problem occurs, when I execute my SQL
SELECT TO_CHAR(to_date('20170801','yyyyMMdd'), 'MM/dd/yyyy') AS TRADE_DATE,
b.securities_code AS SECURITIES_CODE,
b.buy_sell_flag AS SIDE,
SUM(NVL(t.c_balance,0)) AS C_t_balance,
SUM(NVL(b.c_balance,0)) AS C_b_balance,
SUM(NVL(t.c_balance,0)) - SUM(NVL(b.c_balance,0)) AS C_DIFFERENCE,
SUM(NVL(t.p_balance,0)) AS P_t_balance,
SUM(NVL(b.p_balance,0)) AS P_b_balance,
SUM(NVL(t.p_balance,0)) - SUM(NVL(b.p_balance,0)) AS P_DIFFERENCE
FROM b_balance b
FULL OUTER JOIN t_balance t
ON b.securities_code = t.securities_code
AND b.buy_sell_flag = t.buy_sell_flag
GROUP BY b.securities_code,
b.buy_sell_flag
ORDER BY SECURITIES_CODE,
SIDE ;
this returns the following records:
TRADE_DATE SECURITIES_CODE SIDE C_T_BALANCE C_B_BALANCE C_DIFFERENCE P_T_BALANCE P_B_BALANCE P_DIFFERENCE
---------- --------------- ---- ----------- ----------- ------------ ----------- ------------ ------------
08/01/2017 10042 BUY 2100 2100 0 15000 15000 0
08/01/2017 10042 SELL 400 400 0 5000 5000 0
08/01/2017 10043 BUY 300 300 0 0 0 0
08/01/2017 10043 SELL 0 0 0 2500 2500 0
that means the result is being multiplied by number of rows.
I checked on Stack overflow and did't find anything wrong according to this answer.
So what is the wrong in my SQL?
you should join the aggregated result (not aggregate the joined values)
SELECT TO_CHAR(to_date('20170801','yyyyMMdd'), 'MM/dd/yyyy') AS TRADE_DATE,
t1.SECURITIES_CODE,
t1.SIDE,
t1.C_b_balance,
t1.P_b_balance,
t2.C_t_balance,
t2.P_t_balance,
from (
SELECT
b.securities_code AS SECURITIES_CODE,
b.buy_sell_flag AS SIDE,
SUM(NVL(b.c_balance,0)) AS C_b_balance,
SUM(NVL(b.p_balance,0)) AS P_b_balance,
FROM b_balance b
GROUP BY b.securities_code, b.buy_sell_flag ) t1
left join (
SELECT
t.securities_code AS SECURITIES_CODE,
t.buy_sell_flag AS SIDE,
SUM(NVL(t.c_balance,0)) AS C_t_balance,
SSUM(NVL(t.p_balance,0)) AS P_t_balance,
FROM t_balance t
GROUP BY t.securities_code, t.buy_sell_flag
) on t1.securities_code = t2.securities_code and t1.buy_sell_flag = t2.buy_sell_flag
When working with aggregates from different tables, aggregate before joining:
select
date '2017-08-01' as trade_date,
securities_code as securities_code,
buy_sell_flag as side,
nvl(t.sum_c_balance, 0) as c_t_balance,
nvl(b.sum_c_balance, 0) as c_b_balance,
nvl(t.sum_c_balance, 0) - nvl(b.sum_c_balance, 0) as c_difference,
nvl(t.sum_p_balance, 0) as p_t_balance,
nvl(b.sum_p_balance, 0) as p_b_balance,
nvl(t.sum_p_balance, 0) - nvl(b.sum_c_balance, 0) as p_difference
from
(
select
securities_code,
buy_sell_flag,
sum(c_balance) as sum_c_balance,
sum(p_balance) as sum_p_balance
from b_balance
group by securities_code, buy_sell_flag
) b
full outer join
(
select
securities_code,
buy_sell_flag,
sum(c_balance) as sum_c_balance,
sum(p_balance) as sum_p_balance
from t_balance
group by securities_code, buy_sell_flag
) t using (securities_code, buy_sell_flag)
order by securities_code, buy_sell_flag;

How to find the record which is not exists with some criteria in SQL Server?

I have two tables.
ItemRelation table having 30k records
ID ChildID1 ChildID2 ChildID3
------------------------------------------
9 null null null
49 43 50 //43 in childid1, don't want this record too
111 112 113 null
65 68 null null
222 221 223 224
79 null null null
5773 5834 5838 null
F_ItemDailySalesParent having millions of records
ItemID StoreId
-----------------
9 1001 //ItemID 9,41,5773 belongs to 1001 StoreID
41 1001
43 1400 //ItemID 43,45,65,5834 belongs to 1400 StoreID
45 1400
65 1400
68 2000 //ItemID 68,79 belongs to 2000 StoreID
79 2000
5773 1001
5834 1400
5838 2000
I want to show the record ID from ItemRelation table where the ItemID from F_ItemDailySalesParent not present in ItemRelation
ItemID StoreID
-----------------
49 1001
111 1001
65 1001
222 1001
79 1001
9 1400
111 1400
222 1400
79 1400
9 2000
49 2000
111 2000
222 2000
5773 2000
I tried this following query. But this will work without StoreID. But no idea for the above result
select ID from HQMatajer.dbo.ItemRelation ir
where not exists(
select ID,StoreID
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] Fid
where fid.ItemID=ir.ID
or fid.ItemID = ir.ChildID1
or Fid.ItemID=ir.ChildID2
or Fid.ItemID=ir.ChildID3
and time between '2017-01-01 00:00:00.000' and '2017-02-28 00:00:00.000'
group by ItemID,StoreID
)
Update
I have Hqmatajer.dbo.Store that column name of storeCode = F_ItemDailySalesParent.Storeid
Include checking if StoreId matches when using the not exists()
select ID
from HQMatajer.dbo.ItemRelation ir
cross join (select distinct storeCode from Hqmatajer.dbo.Store) s
where not exists(
select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] Fid
where fid.StoreId = s.StoreCode
and [time] between '2017-01-01 00:00:00.000' and '2017-02-28 00:00:00.000'
and ( fid.ItemID=ir.ID
or fid.ItemID=ir.ChildID1
or Fid.ItemID=ir.ChildID2
or Fid.ItemID=ir.ChildID3
)
)
If I understand correctly, you want to start with a list of all stores and items and then filter out the ones that are present.
select i.id, s.storeId
from (select distinct id from HQMatajer.dbo.ItemRelation ir) i cross join
stores s -- assume this exists
where not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.ID and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID1 and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID2 and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID3 and idsp.storeId = s.storeId
);
I did not include the time condition. It is not in your sample data, so it is unclear where it fits.
First get a unique list of ItemIds and unique list of StoreIDs, then you can see which are missing with a left join and a where cross ref table id is null. I'll do it in generic terms so you get the idea:
select s.StoreId, i.ItemId
from Stores s
cross apply Items i
left join ItemRelation ir
on s.StoreId = ir.StoreId
and i.ItemId = ir.ItemId
where ir.Id is null

Howto select (almost) unique values in a specific order

In a trip there are several stops, (a stop = an adress whereone or multiple orders are loaded, or delivered), in a specific order.
For example:
Trip A
Trip_order Action Place Ordernumber
10 Load Paris 394798
20 Load Milan 657748
30 UnLoad Athens 657748
40 Unload Thessaloniki 394798
50 Load Thessaloniki 10142
60 Load Thessaloniki 6577
70 Unload Athens 6577
80 Unload Athens 10412
90 Load Thessaloniki 975147
100 Unload Paris 975147
I want to see the specific stops, in order of the trip:
Load Paris
Load Milan
Unload Athens
Unload Thessaloniki
Load Thessaloniki
Unload Athens
Load Thessaloniki
Unload Paris
I did look at This, but if I do that, I only get the unload Athens, unload Thessaloniki and Load Thessaloniki once.
How do I solve this?
EDIT: 11:11 (UTC +01:00)
To be more specific: these are the tables which present this information:
Trips
Trip_ID
100001
100002
100003
....
Actions
Trip_ID Action MatNr RoOr RoVlg OrderID
100001 1 10 10 1 394798
100001 1 10 20 1 657748
100001 1 10 30 1 657748
100001 1 10 40 1 394798
100001 1 10 50 1 10142
100001 1 10 60 1 6577
100001 1 10 70 1 6577
100001 1 10 80 1 10412
100001 1 10 90 1 975147
100001 1 10 100 1 975147
(Action: 1=load, 4=unload)
The combination of MatNr, RoOr and RoVlg is the order of the Trip.
Orders
OrderID LoadingPlace UnloadingPlace
6577 Thessaloniki Athens
10142 Thessaloniki Athens
394798 Paris Thessaloniki
657748 Milan Athens
975147 Thessaloniki Paris
Try this one. No variables, nothing especially fancy:
select a1.action, a1.place
from trip_a a1
left join trip_a a2
on a2.trip_order =
(select min(trip_order)
from trip_a a3
where trip_order > a1.trip_order)
where a1.action != a2.action or a1.place != a2.place or a2.place is null
Demo here: http://sqlfiddle.com/#!9/4b6dc/13
Hopefully it works on whatever sql you're using, it should, so long as subqueries are supported.
Tt simply finds the next highest trip_id, and joins to it, or joins to null if there is no higher trip_order. It then selects only the rows where either the place, the action, or both are different, or if there is no place in the joined table (a2.place is null).
edited after criteria changed completely
If you want to get the same results, built entirely from your base tables, you can do this:
select
case when a.action = 1 then 'load' when a.action = 0 then 'unload' end as action,
case when a.action = 1 then o.loadingplace when a.action = 0 then o.unloadingplace end as place
from trips t
inner join actions a
on t.trip_id = a.trip_id
inner join orders o
on a.orderid = o.orderid
left join actions a2
on a2.roor =
(select min(roor)
from actions a3
where a3.roor > a.roor)
left join orders o2
on a2.orderid = o2.orderid
where a.action != a2.action
or a2.action is null
or
case when a.action = 1 then o.loadingplace != o2.loadingplace
when a.action = 0 then o.unloadingplace != o2.unloadingplace
end
order by a.roor asc
And here's an updated fiddle: http://sqlfiddle.com/#!9/fdf9c/14
You don't need and you don't want to use distinct for that because we can see in your example that several destinations occur multiple times. What you want: filter out records that match the preceding record in terms of action and place.
This could look something like this:
SELECT *
FROM Trips t1 LEFT JOIN Trips t2 ON t1.Trip_Order = t2.Trip_Order - 10
WHERE t1.Action <> t2.Action OR t1.Place <> t2.Place)
In SQL server, you can get difference of ROW_NUMBER() based trip_order and action,place on and try something like this.
You can use it as a reference to create a similar query in USQL.
Sample Data
DECLARE #Trip TABLE (Trip_order INT, Action VARCHAR(10), Place VARCHAR(50),Ordernumber INT)
INSERT INTO #Trip VALUES
(10 ,'Load', 'Paris', 394798),
(20 ,'Load', 'Milan', 657748),
(30 ,'UnLoad', 'Athens', 657748),
(40 ,'UnLoad', 'Thessaloniki', 394798),
(50 ,'Load', 'Thessaloniki', 10142),
(60 ,'Load', 'Thessaloniki', 6577),
(70 ,'UnLoad', 'Athens', 6577),
(80 ,'UnLoad', 'Athens', 10412),
(90 ,'Load', 'Thessaloniki', 975147),
(100 ,'UnLoad', 'Paris', 975147);
Query
SELECT action,place FROM
(
SELECT *,ROW_NUMBER()OVER(ORDER BY trip_order) - ROW_NUMBER()OVER(ORDER BY action,place) n
FROM #trip
)t
GROUP BY n,action,place
ORDER BY MIN(trip_order)
Try this:
Will work in MySQL:::
SELECT IF(#temp=#temp:=A.TripName, #rank, #rank:=#rank+1) AS rank, A.TripName
FROM (SELECT CONCAT(A.Action, A.Place) AS TripName
FROM TripA A
) A, (SELECT #temp:=0, #rank:=0) AS B
GROUP BY rank
SELECT s.*
FROM stops s LEFT JOIN stops prev ON
( prev.Trip_order < s.Trip_order
AND NOT EXISTS ( SELECT 'a'
FROM stops prev2
WHERE prev2.Trip_order < s.Trip_order
AND prev2.Trip_order > prev.Trip_order
)
)
WHERE s.Action <> COALESCE(prev.Action, '')
OR s.Place <> COALESCE(prev.Place, '')
ORDER BY s.Trip_order
select a1.action,a1.place
from tripa a1,tripa a2
where a2.trip_order = (select min(trip_order) from tripa a3 where trip_order > a1.trip_order)
and (a1.action != a2.action or a1.place != a2.place)
or a2.place is null
This gives you the required result.

Joins and Loops in SQL Server 2008

How to join the below tables to get result like in "Result" table:
Table: Invoice
Inv_No Fk_Rep_ID Inv_Date Inv_Amt
3000 202 10/1/2014 35
3001 194 11/1/2014 40
3002 180 15/1/2014 55
Table :Return
Return_ID FK_Rep_ID Ret_Date Ret_Amt
2000 202 17/1/2014 67
2001 194 15/1/2015 43
Table: Credit
Credit_ID FK_Rep_ID credit_Date credit_Amnt
1000 NULL 4/2/2014 60
1001 202 5/2/2014 12
Table: Debit
Debit_ID FK_Rep_ID Debit_Date Debit_Amnt
400 NULL 4/5/2014 600
4001 194 5/5/2014 110
Table:Receipt_Items
Fk_Rec_No FK_Item_No Item_Type Rec_Item_ID
7787 1000 2 1
7788 2000 1 1
7788 3000 0 2
7788 3001 0 3
7788 3002 0 4
7788 4000 3 5
7788 4001 3 6
7789 1001 2 1
Table :Sales_Rep
Rep_ID Rep_Name
180 Vinu
194 Bibin
202 Salman
Result
Fk_Rec_No Fk_Item_No Item_Type Rep_Name Item_Date Item_Amt
7787 1000 Credit NULL 4/2/2014 -60
7788 2000 Return salman 15/1/2014 -67
7788 3000 Invoice salman 10/1/2014 35
7788 3001 Invoice Bibin 11/1/2014 40
7788 3002 Invoice Vinu 12/1/2014 55
7788 4000 Debit NULL 4/5/2014 600
7788 4001 Debit Bibin 5/5/2014 110
7789 1001 Credit Salman 5/2/2014 -12
Query :
SELECT tt.*,SR.Rep_Name
FROM(SELECT
fk_receipt_no
,fk_item_no
,CASE Item_type
WHEN 0 THEN 'INVOICE'
WHEN 1 THEN 'Return'
WHEN 2 THEN 'Credit'
WHEN 3 THEN 'Debit'
END as ITEM_type,
Case Item_type when 1 then '-'+Cast(Item_Amnt as varchar(50))
when 2 then '-'+Cast(Item_Amnt as varchar(50))
else Cast(Item_Amnt as varchar(50)) End Item_Amnt
,COALESCE(R.FK_Rep_ID,C.FK_Rep_ID,I.FK_Rep_ID) as FK_Rep_ID
,COALESCE(R.Ret_Date,C.Note_Date,I.Inv_Date) as Item_Date
FROM Recp_Item RI LEFT JOIN [Return] R ON RI.FK_Item_no=R.Return_ID
LEFT JOIN Credit C ON RI.FK_Item_No=C.Note_ID
LEFT JOIN Invoice I ON RI.FK_Item_No=I.Inv_No
) tt LEFT JOIN [Sales Rep] SR ON SR.Rep_ID=tt.FK_Rep_ID
You probably should mention that FK_Item_No refers sometimes to different things, like Credit_ID. This kind of multipurpose FK almost always results in a union for each type of item it could represent.
This is pseudo code, I expect you to provide all the tedious join criterias since you should be capable of doing so.
Notice left joins are used to the Rep table since you have some null FK's
In each select aliases are used to normalize the column names such as credit_amnt => Item_Amt. Technically this is only required for the first select in the join so long as the others are in the same order, but I usually do it for all unions for readability.
Select * From
(
Select ri.Fk_Rec_No, Credit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.credit_Date as Item_Date, c.credit_Amnt as Item_Amt
From Credit c inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
Select ri.Fk_Rec_No, Debit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.Debit_Date as Item_Date, d.Debit_Amnt as Item_Amt
From Debit d inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
...
) as typesUnion
Order By Fk_Rec_No, Fk_Item_No