SQL Pivot Query for pivot values - sql

iItemKey Qty FreeQty Unit TaxCatKey TaxVal
7 1 1 1 1 4.00
7 1 1 1 1 1.00
I need output as
iItemKey Qty FreeQty Unit TaxCatKey VAT A.VAt
7 1 1 1 1 4.00 1.00
Here is my query. But i get VAT and A.Vat values as Null
WITH T
AS (SELECT T_ItemRequestSub.iItemKey, T_ItemRequestSub.Qty, T_ItemRequestSub.FreeQty, T_ItemRequestSub.Unit, T_ItemRequestSub.TaxType,
M_Mt_TaxCategorySub.iTaxCatKey , M_Mt_TaxCategorySub.iTaxVal
FROM T_ItemRequestSub INNER JOIN
T_ItemRequest ON T_ItemRequestSub.iReqKey = T_ItemRequest.iKey INNER JOIN
M_Mt_TaxCategory ON T_ItemRequestSub.TaxType = M_Mt_TaxCategory.iKey INNER JOIN
M_Mt_TaxCategorySub ON M_Mt_TaxCategory.iKey = M_Mt_TaxCategorySub.iTaxCatKey where T_ItemRequestSub.iKey = 2)
SELECT *
FROM T PIVOT ( sum (iTaxVal) FOR TaxType IN (
[Vat],
[A.Vat]
) ) AS pvt
Please Help

The only distinct value is TaxVal so I suspect you are trying to do following:
WITH Src AS --Your source table
(
SELECT * FROM (VALUES
(7, 1, 1, 1, 1, 4.00),
(7, 1, 1, 1, 1, 1.00)
)T(iItemKey, Qty, FreeQty, Unit, TaxCatKey, TaxVal)
)
SELECT * FROM
(
SELECT iItemKey, Qty, FreeQty, Unit, TaxCatKey, TaxVal, CASE WHEN TaxVal = 1.00 THEN 'VAT' ELSE 'A.VAT' END Col
FROM Src
) T
PIVOT (MAX(TaxVal) FOR Col IN ([VAT], [A.VAT])) P

Related

SQL Query for repeated row set NULL

I have Parent as well as Child table...
Master table : SalesInvoice
ID ToatlQty Unit
1 100 BOX
2 110 BOX
Detail Table : SalesInvoiceDetail
DetailsID ID ItemMasterID Qty Unit
01 1 000000001010 25 BOX
02 1 000000001010 25 BOX
03 1 000000001013 30 BOX
04 2 000000001014 50 BOX
I want output like
ID ItemMasterID Unit TotalQty Qty
1 000000001010 BOX 100 50
1 000000001013 BOX 0 30
2 000000001014 BOX 110 50
I have got TotalQty '100' two time for ID '1' but i want set 0 if it is repeated.
The query below is built with following assumptions: you need TotalQty 0 for repeated values if they have higher ItemMasterID, sum Qty by ID and ItemMasterID (based on the output). Let me know if they are wrong. The query includes sample data and can be run as is:
-- sample data
;with SalesInvoice as (
select * from (values
(1, 100, 'BOX'),
(2, 110, 'BOX')
) t(ID, TotalQty, Unit)
),
SalesInvoiceDetail as (
select * from (values
(1, 1, '000000001010', 25, 'BOX'),
(2, 1, '000000001010', 25, 'BOX'),
(3, 1, '000000001013', 30, 'BOX'),
(4, 2, '000000001014', 50, 'BOX')
) t (DetailsID, ID, ItemMasterID, Qty, Unit)
)
-- solution
select
t.ID,
t.ItemMasterID,
SalesInvoice.Unit,
TotalQty =
case when ROW_NUMBER() over(partition by t.id order by ItemMasterID) = 1
then TotalQty else 0 end,
t.Qty
from (
select id, ItemMasterID, Qty = sum(qty) from SalesInvoiceDetail
group by id, ItemMasterID
) t
join SalesInvoice on t.id = SalesInvoice.ID
SELECT A.Id ,
A.ItemMasterID ,
A.Unit ,
ISNULL(B.ToatlQty, 0) AS ToatlQty ,
A.Qty
FROM ( SELECT Id ,
ItemMasterID ,
Unit ,
SUM(Qty) AS Qty
FROM SalesInvoiceDetail A
GROUP BY Id ,
ItemMasterID ,
Unit
) A
LEFT JOIN SalesInvoice B ON A.ID = B.ID

How to join tables, concatenate some data

I have two tables:
1. indexes and quantity of indexes
2. indexes and quantity of indexes with specified boxcodes. Boxcode is a number of box, which box contains indexes.
1. input table 1
item_id quantity
1 10
2 15
3 5
1 5
1 5
2 5
3 5
sum:
1 - 20
2 - 20
3 - 10
2. input table 2
item_id quantity boxcode
1 3 abc
2 2 abc
1 8 def
3 10 ghi
1 9 ghi
2 9 def
2 8 ghi !!!!!!!
1 item_id once on 1 boxcode
I want to get result:
3. result
item_id quantity boxcodes
1 10 abc/3, def/7
2 15 abc/2, def/9, ghi/4
3 5 ghi/5
1 5 def/1, ghi/4
1 5 ghi/5
2 5 ghi/4 !!!!!!!!
3 5 ghi/5
Records from table 1 must be in the same order.
I have no idea how it can be done.
Any suggestion?
CREATE TABLE #input1
(
rownum int,
item_id int,
quantity int
)
CREATE TABLE #input2
(
item_id int,
quantity int,
boxcode varchar(10)
)
INSERT INTO #input1 VALUES (1,1,10)
INSERT INTO #input1 VALUES (2,2,15)
INSERT INTO #input1 VALUES (3,3,5)
INSERT INTO #input1 VALUES (4,1,5)
INSERT INTO #input1 VALUES (5,1,5)
INSERT INTO #input1 VALUES (6,2,5)
INSERT INTO #input1 VALUES (7,3,5)
INSERT INTO #input2 VALUES (1,3, 'abc')
INSERT INTO #input2 VALUES (2,2, 'abc')
INSERT INTO #input2 VALUES (1,8, 'def')
INSERT INTO #input2 VALUES (3,10, 'ghi')
INSERT INTO #input2 VALUES (1,9, 'ghi')
INSERT INTO #input2 VALUES (2,9, 'def')
INSERT INTO #input2 VALUES (2,8, 'ghi')
select * from #input1
select * from #input2
drop table #input1
drop table #input2
result
Thanks,
Weird, but it works:
;WITH rec1 AS (
SELECT rownum,
item_id,
1 as q,
1 as [Level],
quantity
from #input1
UNION ALL
SELECT r.rownum,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec1 r
INNER JOIN #input1 i
ON r.rownum = i.rownum AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), rec2 AS (
SELECT boxcode,
item_id,
1 as q,
1 as [Level],
quantity
from #input2
UNION ALL
SELECT r.boxcode,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec2 r
INNER JOIN #input2 i
ON r.boxcode = i.boxcode AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), cte1 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn
FROM rec1
), cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn
FROM rec2
), final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM cte1 c1
INNER JOIN cte2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
)
SELECT DISTINCT
f.rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
Output for dataset you have provided:
rownum item_id quantity boxcodes
1 1 10 abc/3, def/7
2 2 15 abc/2, def/9, ghi/4
3 3 5 ghi/5
4 1 5 def/1, ghi/4
5 1 5 ghi/5
6 2 5 ghi/4
7 3 5 ghi/5
The main idea is to spread quantity in both tables for a small parts 1. Than add row number, then join and get result.
A solution (but it's totally based on gofr1's answer, to be honest !), to simplify a bit, would be to create a Numbers table, with as many numbers as you want.
CREATE TABLE Numbers(Number INT PRIMARY KEY);
INSERT Numbers
SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_columns;
That would just avoid the 2 recursive CTEs.
You could then use the same logic as gofr1 :
with rec1 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn,
rownum,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input1
join Numbers n on n.Number <= quantity
)
, rec2 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn,
boxcode,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input2
join Numbers n on n.Number <= quantity
),
final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM rec1 c1
INNER JOIN rec2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
),
stuffed as (
SELECT
distinct rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
group by item_id, quantity, boxcodes, rownum)
select *
from stuffed
order by rownum

Filter the same payments by different persons - SQL

can you please help me with the following problem :
i have a table Payments :
Id ROLE Payment
1 A 100
1 A 100
1 R 50
1 R 50
1 R 50
2 A 100
2 A 100
2 R 50
2 R 50
2 R 100
Now the ID is one cash loan ( so we have two of them). Role A/R means there are two ppls paying that loan (A-one guy, R- second guy).
Now i need to SUM all the payments for specific ID which is still ok, BUT when the payment for role A is same as for role R i want to count for SUM just one of them..
I was trying to solve it with inner join on the same table where id and payment is the same and role is different but still have the problem that when i have this:
2 A 100
2 A 100
2 R 100
the result is not 100 but 200 ( i will count (A100,R100) = 100 , A100 = 100)
Thank you for any help
UPDATE:
#Giorgi Nakeuri - for given example, the result should looke like :
ID Payment
1 350
2 300
UPDATE : for better understanding : In the given example(for id 2)i have 100payment for R and A so we count it just once = 100, then i have one 100payment just for A = 100, and two payments 50 just for R = 100, 100+100+100 = 300 payment for id 2
Try:
DECLARE #t TABLE ( ID INT, R CHAR(1), P INT )
INSERT INTO #t
VALUES ( 1, 'A', 100 ),
( 1, 'A', 100 ),
( 1, 'R', 50 ),
( 1, 'R', 50 ),
( 1, 'R', 50 ),
( 2, 'A', 100 ),
( 2, 'A', 100 ),
( 2, 'R', 50 ),
( 2, 'R', 50 ),
( 2, 'R', 100 );
WITH cte
AS ( SELECT id ,
P ,
SUM(CASE WHEN R = 'A' THEN 1 ELSE 0 END) cd1 ,
SUM(CASE WHEN R = 'R' THEN 1 ELSE 0 END) cd2
FROM #t
GROUP BY id ,
P
)
SELECT ID ,
SUM(P * CASE WHEN cd1 > cd2 THEN cd1 ELSE cd2 END) AS Sum
FROM cte
GROUP BY ID
Output:
ID Sum
1 350
2 300
Have you try to distinct the query?
SELECT distinct yourField from yourTable

SQL amount consumed query?

How do you do a query to calculate how much of an item has been consumed (used up)?
We can find the qty of each item that we purchased in a purchases table with columns Id, ProductId, Qty (decimal), Date
Id, ProductId, Qty, Date
1, 1, 10, 1/1/11
2, 1, 5, 2/2/11
3, 1, 8, 3/3/11
And how do you then add a count of how many of each row in the purchase table have been consumed - assuming strict FIFO? So in the above example if we know that 14 have been consumed the output would be:
Id, ProductId, Qty, Date, Consumed
1, 1, 10, 1/1/11, 10
2, 1, 5, 2/2/11, 4
3, 1, 8, 3/3/11, 0
Hopefully that explains what I mean by an amount consumed query - we know 14 were consumed and that the first purchase was for 10, so all 10 have been consumed. The next purchase was for 5 so we know that 4 of those have been consumed.
Theres two places I can get the consumed data from - the ConsumedItems table: columns Id, ProductId, QtyUsed, Date), or from the ConsumedSummaryView with columns ProductId, QtyUsed (this is the sum of ConsumedItems.QtyUsed)
Sample table and view
create table purchases (Id int, ProductId int, Qty int, Date datetime)
insert purchases select 1, 1, 10, '1/1/11'
insert purchases select 2, 1, 5, '2/2/11'
insert purchases select 3, 1, 8, '3/3/11'
create view ConsumedSummaryView as select ProductID = 1, QtyUsed = 14
The query
;with p as (
select *, rn=ROW_NUMBER() over (partition by productid order by date, id)
from purchases)
, tmp(Id, ProductId, Qty, Date, rn, ToGo, Consumed) as (
select p.Id, p.ProductId, p.Qty, p.Date, cast(1 as bigint),
CAST(ISNULL(v.qtyused,0) - p.Qty as decimal(20,10)),
cast(case
when v.qtyused >= p.Qty Then p.Qty
when v.qtyused > 0 then v.qtyused
else 0 end as decimal(20,10))
from p
left join ConsumedSummaryView v on p.ProductId = v.productId
where rn=1
union all
select p.Id, p.ProductId, p.Qty, p.Date, cast(p.rn as bigint),
cast(ISNULL(tmp.toGo,0) - p.Qty as decimal(20,10)),
cast(case
when tmp.toGo >= p.Qty Then p.Qty
when tmp.toGo > 0 then tmp.toGo
else 0 end as decimal(20,10))
from tmp
--inner join p on p.rn=tmp.rn+1
inner join p on p.rn=tmp.rn+1 and p.productid = tmp.ProductId
)
select Id, ProductId, Qty, Date, Consumed
from tmp
order by rn
Output
Id ProductId Qty Date Consumed
----------- ----------- ----------- ----------------------- -----------
1 1 10 2011-01-01 00:00:00.000 10
2 1 5 2011-02-02 00:00:00.000 4
3 1 8 2011-03-03 00:00:00.000 0
A little different approach than Richard's, but I'm not sure which will perform better:
SELECT
Purchases.Id,
Purchases.ProductId,
Purchases.Qty,
Purchases.Date,
CASE
WHEN COALESCE (PreviousPurchases.PreviousUsed, 0) + Qty < ConsumedSummaryView.QtyUsed THEN Qty
ELSE
CASE
WHEN ConsumedSummaryView.QtyUsed - COALESCE (PreviousPurchases.PreviousUsed, 0) < 0 THEN 0
ELSE ConsumedSummaryView.QtyUsed - COALESCE (PreviousPurchases.PreviousUsed, 0)
END
END AS Used
FROM
Purchases
INNER JOIN ConsumedSummaryView ON Purchases.ProductId = ConsumedSummaryView.ProductId
LEFT OUTER JOIN (
SELECT
SUM(Purchases_2.Qty) AS PreviousUsed,
Purchases_1.Id
FROM
Purchases AS Purchases_2
INNER JOIN Purchases AS Purchases_1 ON Purchases_2.Id < Purchases_1.Id
AND Purchases_2.ProductId = Purchases_1.ProductId
GROUP BY
Purchases_1.Id
) AS PreviousPurchases ON Purchases.Id = PreviousPurchases.Id

Generating order statistics grouped by order total

Hopefully I can explain this correctly. I have a table of line orders (each line order consists of quantity of item and the price, there are other fields but I left those out.)
table 'orderitems':
orderid | quantity | price
1 | 1 | 1.5000
1 | 2 | 3.22
2 | 1 | 9.99
3 | 4 | 0.44
3 | 2 | 15.99
So to get order total I would run
SELECT SUM(Quantity * price) AS total
FROM OrderItems
GROUP BY OrderID
However, I would like to get a count of all total orders under $1 (just provide a count).
My end result I would like would be able to define ranges:
under $1, $1 - $3, 3-5, 5-10, 10-15, 15.. etc;
and my data to look like so (hopefully):
tunder1 | t1to3 | t3to5 | t5to10 | etc
10 | 500 | 123 | 5633 |
So that I can present a piechart breakdown of customer orders on our eCommerce site.
Now I can run individual SQL queries to get this, but I would like to know what the most efficient 'single sql query' would be. I am using MS SQL Server.
Currently I can run a single query like so to get under $1 total:
SELECT COUNT(total) AS tunder1
FROM (SELECT SUM(Quantity * price) AS total
FROM OrderItems
GROUP BY OrderID) AS a
WHERE (total < 1)
How can I optimize this? Thanks in advance!
select
count(case when total < 1 then 1 end) tunder1,
count(case when total >= 1 and total < 3 then 1 end) t1to3,
count(case when total >= 3 and total < 5 then 1 end) t3to5,
...
from
(
select sum(quantity * price) as total
from orderitems group by orderid
);
you need to use HAVING for filtering grouped values.
try this:
DECLARE #YourTable table (OrderID int, Quantity int, Price decimal)
INSERT INTO #YourTable VALUES (1,1,1.5000)
INSERT INTO #YourTable VALUES (1,2,3.22)
INSERT INTO #YourTable VALUES (2,1,9.99)
INSERT INTO #YourTable VALUES (3,4,0.44)
INSERT INTO #YourTable VALUES (3,2,15.99)
SELECT
SUM(CASE WHEN TotalCost<1 THEN 1 ELSE 0 END) AS tunder1
,SUM(CASE WHEN TotalCost>=1 AND TotalCost<3 THEN 1 ELSE 0 END) AS t1to3
,SUM(CASE WHEN TotalCost>=3 AND TotalCost<5 THEN 1 ELSE 0 END) AS t3to5
,SUM(CASE WHEN TotalCost>=5 THEN 1 ELSE 0 END) AS t5andup
FROM (SELECT
SUM(quantity * price) AS TotalCost
FROM #YourTable
GROUP BY OrderID
) dt
OUTPUT:
tunder1 t1to3 t3to5 t5andup
----------- ----------- ----------- -----------
0 0 0 3
(1 row(s) affected)
WITH orders (orderid, quantity, price) AS
(
SELECT 1, 1, 1.5
UNION ALL
SELECT 1, 2, 3.22
UNION ALL
SELECT 2, 1, 9.99
UNION ALL
SELECT 3, 4, 0.44
UNION ALL
SELECT 4, 2, 15.99
),
ranges (bound) AS
(
SELECT 1
UNION ALL
SELECT 3
UNION ALL
SELECT 5
UNION ALL
SELECT 10
UNION ALL
SELECT 15
),
rr AS
(
SELECT bound, ROW_NUMBER() OVER (ORDER BY bound) AS rn
FROM ranges
),
r AS
(
SELECT COALESCE(rf.rn, 0) AS rn, COALESCE(rf.bound, 0) AS f,
rt.bound AS t
FROM rr rf
FULL JOIN
rr rt
ON rt.rn = rf.rn + 1
)
SELECT rn, f, t, COUNT(*) AS cnt
FROM r
JOIN (
SELECT SUM(quantity * price) AS total
FROM orders
GROUP BY
orderid
) o
ON total >= f
AND total < COALESCE(t, 10000000)
GROUP BY
rn, t, f
Output:
rn f t cnt
1 1 3 1
3 5 10 2
5 15 NULL 1
, that is 1 order from $1 to $3, 2 orders from $5 to $10, 1 order more than $15.