how do I use Pivot in Sql Server? - sql

I have a table that stores the data as follows:
username status
user1 HT
user2 EC
user1 PR
user1 EC
user2 HT
I need result something like this
username HT EC PR GrandTotal
user1 1 1 1 3
user2 1 1 0 2
Total 2 2 1 5
please anybody help me. Thanks in advance

It is simple when Status values are fixed.
select
username,
sum(case when status='HT' then 1 else 0 end) as HT,
sum(case when status='EC' then 1 else 0 end) as EC,
sum(case when status='PR' then 1 else 0 end) as PR,
count(*) as total
from
table
group by
username
WITH ROLLUP
If they are dynamic, you need to use http://beyondrelational.com/modules/2/blogs/70/posts/10840/dynamic-pivot-in-sql-server-2005.aspx

USE AdventureWorks
GO
-- Creating Test Table
CREATE TABLE Product(Cust VARCHAR(25), Product VARCHAR(20), QTY INT)
GO
-- Inserting Data into Table
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',2)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','SODA',6)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','MILK',1)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','BEER',12)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','MILK',3)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','BEER',24)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',3)
GO
-- Selecting and checking entires in table
SELECT *
FROM Product
GO
-- Pivot Table ordered by PRODUCT
SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT
GO
-- Pivot Table ordered by CUST
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt
ORDER BY CUST
GO
-- Unpivot Table ordered by CUST
SELECT CUST, PRODUCT, QTY
FROM
(
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT
( SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt) p
UNPIVOT
(QTY FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)
) AS Unpvt
GO
-- Clean up database
DROP TABLE Product
GO
ResultSet:
-- Selecting and checking entires in table
Cust Product QTY
------------------------- -------------------- -----------
KATE VEG 2
KATE SODA 6
KATE MILK 1
KATE BEER 12
FRED MILK 3
FRED BEER 24
KATE VEG 3
-- Pivot Table ordered by PRODUCT
PRODUCT FRED KATE
-------------------- ----------- -----------
BEER 24 12
MILK 3 1
SODA NULL 6
VEG NULL 5
-- Pivot Table ordered by CUST
CUST VEG SODA MILK BEER CHIPS
------------------------- ----------- ----------- ----------- ----------- -----------
FRED NULL NULL 3 24 NULL
KATE 5 6 1 12 NULL
-- Unpivot Table ordered by CUST
CUST PRODUCT QTY
------------------------- -------- -----------
FRED MILK 3
FRED BEER 24
KATE VEG 5
KATE SODA 6
KATE MILK 1
KATE BEER 12 12
Reference:
http://blog.sqlauthority.com/2008/06/07/sql-server-pivot-and-unpivot-table-examples/

Related

How to find out first product item client purchased whose bought specific products?

I want to write a query to locate a group of clients whose purchased specific 2 product categories, at the same time, getting the information of first transaction date and first item they purchased. Since I used group by function, I could only get customer id but not first item purchase due to the nature of group by. Any thoughts to solve this problem?
What I have are transaction tables(t), customer_id tables(c) and product tables(p). Mine is SQL server 2008.
Update
SELECT t.customer_id
,t.product_category
,MIN(t.transaction_date) AS FIRST_TRANSACTION_DATE
,SUM(t.quantity) AS TOTAL_QTY
,SUM(t.sales) AS TOTAL_SALES
FROM transaction t
WHERE t.product_category IN ('VEGETABLES', 'FRUITS')
AND t.transaction_date BETWEEN '2020/01/01' AND '2022/09/30'
GROUP BY t.customer_id
HAVING COUNT(DISTINCT t.product_category) = 2
**Customer_id** **transaction_date** **product_category** **quantity** **sales**
1 2022-05-30 VEGETABLES 1 100
1 2022-08-30 VEGETABLES 1 100
2 2022-07-30 VEGETABLES 1 100
2 2022-07-30 FRUITS 1 50
2 2022-07-30 VEGETABLES 2 200
3 2022-07-30 VEGETABLES 3 300
3 2022-08-01 FRUITS 1 50
3 2022-08-05 FRUITS 1 50
4 2022-08-07 FRUITS 1 50
4 2022-09-05 FRUITS 2 100
In the above, what I want to show after executing the SQL query is
**Customer_id** **FIRST_TRANSACTION_DATE** **first_product_category** **TOTAL_QUANTITY** **TOTAL_SALES**
2 2022-07-30 VEGETABLES, FRUITS 4 350
3 2022-07-30 VEGETABLES 5 400
Customer_id 1 and 4 will not be shown as they only purchased either vegetables or fruits but not both
Check now, BTW need find logic with product_category
select CustomerId, transaction_date, product_category, quantity, sales
from(
select CustomerId, transaction_date, product_category , sum(quantity) over(partition by CustomerId ) as quantity , sum(sales) over(partition by CustomerId ) as sales, row_number() over(partition by CustomerId order by transaction_date ASC) rn
from(
select CustomerId, transaction_date, product_category, quantity, sales
from tablee t
where (product_category = 'FRUITS' and
EXISTS (select CustomerId
from tablee tt
where product_category = 'VEGETABLES'
and t.CustomerId = tt.CustomerId)) OR
(product_category = 'VEGETABLES' and
EXISTS (select CustomerId
from tablee tt
where product_category = 'FRUITS'
and t.CustomerId = tt.CustomerId)))x)over_all
where rn = 1;
HERE is FIDDLE

Still return records if unsatisfied cross join condition

I have a table (Name: Sales) about when and what customers purchase:
Date C_Name Thing
02/21 A Fruit
02/26 A Doll
02/27 A Drink
02/28 B Book
06/03 B Fruit
06/13 B Shoes
06/10 C Shoes
07/07 A Tablet
07/11 A Chair
07/20 A Sofa
07/21 A Coat
And I need to return the first four items purchased by every customer each month, if the items were purchased in a month were less than four, the return should still see the records. Desired Output:
Date(Month) C_Name Thing1 Thing2 Thing3 Thing4
2 A Fruit Doll Drink
2 B Book
6 B Fruit Shoes
.
.
.
7 A Tablet Chair Sofa Coat
My current code is as follow, which only can show if the customer purchased equal or more than four objects:
SELECT MONTH(s1.Date) AS Date(Month), s1.C_Name, s1.Thing, s2.Thing, s3.Thing, s4.Thing
FROM Sales as s1, Sales as s2, Sales as s3, Sales as s4
ON s1.C_Name = s2.C_Name = s3.C_Name = s4.C_Name
AND s1.Date<s2.Date
AND s2.Date<s3.Date
AND s3.Date<s4.Date
AND MONTH(s1.Date) = MONTH(s2.Date)= MONTH(s3.Date)= MONTH(s4.Date);
Is there any solution for me to change my code?
row_number + pivot
with
t1 as
(
select *, format(Date,'yyyy-MM') as month
from Sales
),
t2 as
(
select 'Thing'
+ cast(row_number() over
(
partition by C_Name, month
order by Date
) as varchar(10)
) as thing_id
,month
,C_Name
,Thing
from t1
)
select *
from t2 pivot (min(Thing) for thing_id in ([Thing1],[Thing2],[Thing3],[Thing4])) as p
month
C_Name
Thing1
Thing2
Thing3
Thing4
2021-02
A
Fruit
Doll
Drink
2021-07
A
Tablet
Chair
Sofa
Coat
2021-02
B
Book
2021-06
B
Fruit
Shoes
2021-06
C
Shoes
Fiddle

How to insert unique rows from one table to another table in postgres?

I want to insert the rows which are unique from product_table to bill_table_unique.
And then duplicated rows has to be moved from product_table to bill_table_duplicate.
Once all the data has been moved means the product_table entries have to be cleared (Only the moved rows has to be cleared)
Input:
product_table
id bill_id entry_date product_name stock
-------------------------------------------------------------------
1 009 2020-12-11 02:05:20.09876 apple 5
2 009 2020-12-11 02:05:20.09876 apple 5
3 009 2020-09-11 02:05:20.09876 apple 5
4 002 2020-12-11 02:05:20.09876 berry 5
5 002 2020-12-11 02:05:20.09876 berry 5
6 004 2020-12-11 10:05:20.09876 pineapple 1
7 006 2020-12-11 10:05:20.09876 pineapple 4
STEP 1: to insert the rows which are unique from product_table to bill_table_unique.
Output: bill_table_unique
id bill_id entry_date product_name stock
-------------------------------------------------------------------
1 009 2020-12-11 02:05:20.09876 apple 5
2 009 2020-09-11 02:05:20.09876 apple 5
3 002 2020-12-11 02:05:20.09876 berry 5
4 004 2020-12-11 10:05:20.09876 pineapple 1
5 006 2020-12-11 10:05:20.09876 pineapple 4
STEP 2: then duplicated rows has to be moved from product_table to bill_table_duplicate.
Output: bill_table_duplicate
id bill_id entry_date product_name stock
---------------------------------------------------------------------
1 009 2020-12-11 02:05:20.09876 apple 5
2 002 2020-12-11 02:05:20.09876 berry 5
STEP 3: once all the data has been moved means the product_table entries have to be cleared (Only the moved rows has to be cleared)
Output: product_table
id bill_id entry_date product_name stock
--------------------------------------------------------------------
Use two queries as follows:
Insert into bill_table_unique
Select distinct bill_id, entry_date, product_name, stock
From product_table;
Insert into bill_table_duplicate
Select bill_id, entry_date, product_name, stock
From
(Select bill_id, entry_date, product_name, stock,
Row_number() over (partirion by bill_id, entry_date, product_name, stock
order by null) as rn
From product_table) t
Where rn >= 2;
CREATE TABLE foo (id INTEGER PRIMARY KEY, bill_id TEXT NOT NULL,
entry_date TIMESTAMP NOT NULL, product_name TEXT NOT NULL,
stock INTEGER NOT NULL);
\copy foo from stdin
1 009 2020-12-11 02:05:20.09876 apple 5
2 009 2020-12-11 02:05:20.09876 apple 5
3 009 2020-09-11 02:05:20.09876 apple 5
4 002 2020-12-11 02:05:20.09876 berry 5
5 002 2020-12-11 02:05:20.09876 berry 5
6 004 2020-12-11 10:05:20.09876 pineapple 1
7 006 2020-12-11 10:05:20.09876 pineapple 4
\.
CREATE TABLE foo1 (id SERIAL PRIMARY KEY, bill_id TEXT NOT NULL,
entry_date TIMESTAMP NOT NULL, product_name TEXT NOT NULL,
stock INTEGER NOT NULL);
CREATE TABLE foo2 (id SERIAL PRIMARY KEY, bill_id TEXT NOT NULL,
entry_date TIMESTAMP NOT NULL, product_name TEXT NOT NULL,
stock INTEGER NOT NULL);
WITH a AS (
SELECT min(id) id, bill_id, entry_date, product_name, stock
FROM foo GROUP BY bill_id, entry_date, product_name, stock
), b AS (
INSERT INTO foo1 (bill_id, entry_date, product_name, stock)
SELECT bill_id, entry_date, product_name, stock FROM a)
INSERT INTO foo2 (bill_id, entry_date, product_name, stock)
SELECT f.bill_id, f.entry_date, f.product_name, f.stock
FROM foo f LEFT JOIN a USING (id) WHERE a.id IS NULL;
First CTE in the WITH query computes which rows to transfer into table foo1. I picked the rows with minimum id.
Second CTE inserts the rows.
Third CTE inserts the rows that were not inserted in the previous step, these are the duplicates.
If the number of rows isn't too large, this should use a hashaggregate and hashjoin, and be much faster than a query using a sort. Also the deduplication is only done once.
STEP 3: once all the data has been moved means the product_table entries have to be cleared (Only the moved rows has to be cleared)
Since the question specifies no conditions about which rows should not be moved, then all rows in the original table have been moved, so:
TRUNCATE TABLE product_table;
I would use row_number() to enumerate the rows in the original table. Then the insertions into the other tables only rely simple filters:
WITH pt AS (
SELECT pt.*,
ROW_NUMBER() OVER (PARTITION BY bill_id, entry_date, product_name, stock ORDER BY id) as seqnum
FROM product_table pt
),
btu AS (
INSERT INTO bill_table_unique (bill_id, entry_date, product_name, stock)
SELECT bill_id, entry_date, product_name, stock
FROM pt
WHERE seqnum = 1
)
INSERT INTO bill_table_duplicate (bill_id, entry_date, product_name, stock)
SELECT bill_id, entry_date, product_name, stock
FROM pt
WHERE seqnum > 1;

How to sum and subtract values using update with group by Sql query?

MyTable:
item_name Qty item_area
Belts 2 India
Shoes 20 US
T-Shirt 10 India
T-Shirt 12 US
T-Shirt 25 US
I get this by select group by query
SELECT item_name, Sum(item_qty) as Qty, item_area
FROM dbo.item_stocks
group by item_area, item_name
and my output is below:
item_name Qty item_area
Belts 2 India
Shoes 20 US
T-Shirt 10 India
T-Shirt 37 US
Now i need to subtract and update .How can i do this ?
For eg. i want to subtract 5 T-Shirt ,it will update in MyTable?
T-shirt=37-5=32
how can i update in MyTable?
You can try to use ROW_NUMBER window function to choose any one row to subtract.
;WITH CTE AS (
SELECT *,ROW_NUMBER() over(PARTITION BY item_name,item_area order by Qty desc)rn
FROM item_stocks
)
update CTE
set Qty = Qty - 5
where rn = 1 and item_name = 'T-Shirt' and item_area = 'US'
SELECT item_name, Sum(Qty) as Qty, item_area
FROM item_stocks
group by item_area, item_name
just add a column which denotes in/Out, say INOUT
don't update row, just add new row for the stock sale.
Here,
INOUT = 1 denotes IN and 2 denotes OUT
table structure like
item_name Qty item_area INOUT
Belts 2 India 1
Shoes 20 US 1
T-Shirt 10 India 1
T-Shirt 12 US 1
T-Shirt 25 US 1
T-Shirt 5 US 2
now your query looks like that
SELECT item_name,
Sum(case when INOUT = 1 then item_qty else (0-item_qty) end) as Qty,
item_area
FROM dbo.item_stocks
group by item_area, item_name

Min per group in SQL but with a caveat

I've got this table in SQL below and I need to return "the car vendors that will never be used if the car purchaser is a rational person" or "The vendor for which all car prices are more expensive then others". I've tried to do the idea of joining with itself but I am unable to get it work. The resulting output should be vendor 3 since its price for car 3 and 4 is more expensive than the other option.
id car_vendor_id vendor_name car_id price
---------------------------------------------
1 1 Vendor 1 1 25000
2 1 Vendor 1 2 40000
3 2 Vendor 2 2 35000
4 2 Vendor 2 3 25000
5 3 Vendor 3 3 28000
6 3 Vendor 3 4 40000
7 4 Vendor 4 4 35000
8 4 Vendor 4 5 20000
9 5 Vendor 5 5 18000
10 5 Vendor 5 6 32000
11 6 Vendor 6 6 30000
12 6 Vendor 6 7 20000
One method is row_number() and aggregation:
select car_vendor_id, vendor_name
from (select t.*,
rank() over (partition by car_id order by price) as seqnum
from t
) t
group by car_vendor_id, vendor_name
having min(seqnum) > 1;
The having clause is selecting rows where the vendor has no cars that are "first" based on price.
The following query uses a CTE to work out the price order for each car, so the most expensive is 1.
It then excludes rows where there is a row for the vendor where they are not the most expensive, and lastly checks they are are not the only vendor for a car.
declare #Car table(Vendor int, Car int, Price int)
insert #Car values (1,1,25000),(1,2,40000),(2,2,35000),(2,3,25000),(3,3,28000),(3,4,40000),(4,4,35000),(4,5,20000),(5,5,18000),(5,6,32000),(6,6,30000),(6,7,20000)
;with Price as (
select *, row_number() over(partition by Car order by Price desc) as r from #Car Car
)
select * from Price
where not exists(select * from Price p2 where p2.Vendor=Price.Vendor and p2.r>1)
and Vendor not in (
select Vendor from #Car where Car in (select Car from #Car group by Car having count(*)=1)
)
Check on the next query:
declare #car table(Vendor int, Car int, Price int);
insert #car
values
(1,1,25000),(1,2,40000),(2,2,35000),(2,3,25000),
(3,3,28000),(3,4,40000),(4,4,35000),(4,5,20000),
(5,5,18000),(5,6,32000),(6,6,30000),(6,7,20000);
with
a as (
select
vendor, price,
count(*) over(partition by car) cq,
count(*) over(partition by vendor) vcq,
max(price) over(partition by car) xcp
from #car
)
select vendor
from a
where cq > 1 and xcp = price
group by vendor, vcq
having count(*) = vcq;
To try the query online, please click here.