Customized Pivot in sql 2014 - sql

I am trying to do PIVOT on sql, where two columns value has to be aggreagated for each year.
The below code gives perfect result.
DECLARE #TABLE TABLE
(
SKU VARCHAR(10),
YYMM VARCHAR(50),
BRAND VARCHAR(50),
AMT DECIMAL,
QTY INT
)
INSERT INTO #TABLE
SELECT '104591168', '2015-January', 'abott',200, 2 UNION ALL
SELECT '104580709', '2016-January', 'GSK',159 , 2 UNION ALL
SELECT '104720038', '2017-January', 'RANBAXCY',169, 2 UNION ALL
SELECT '10467011A', '2018-January', 'abott',185, 2 UNION ALL
SELECT '104590691', '2019-January', 'abott',256 , 10
SELECT *
FROM(
SELECT BRAND, sku, QTY, YYMM,AMT/QTY AS AVGPR
FROM #TABLE
) AS src
PIVOT(
sum(QTY)
for [YYMM] IN( [2015-January], [2016-January], [2017-January] /* add other moneths here */ )
) AS Pivoted
and result look like
But how can i see AVGPR in same pivot way as like sum(qty).
when i tried like
code:
SELECT *
FROM(
SELECT BRAND, sku, QTY, YYMM
FROM #TABLE
) AS src
PIVOT(
sum(QTY),
SUM(AVG)
for [YYMM] IN( [2015-January], [2016-January], [2017-January] /* add other moneths here */ )
) AS Pivoted
i am getting Incorrect syntax error.
Please help
I have a data like this.
SKU YYMM BRAND Sales Cost QTY AVGPRICE
101110028 1/1/2017 ABOTT 15.7 5.73 1 15.7
101110028 2/1/2017 ABOTT 16.33 5.66 1 16.33
101110028 3/1/2017 ABOTT 31.2 11.34 2 15.6
and
I AM TRYING TO DISPLAY LIKE THIS
Sum of QTY Sum of Avg Price
BRAND PNO 1/1/2017 2/1/2017 3/1/2017 1/1/2017 2/1/2017 3/1/2017
PAGID 101110028 0 2 1 15.7 16.33 15.6
Pivot on Quantity and then sum of avg for a same YYMM In a row

I would just use conditional aggregation:
SELECT brand, sku,
SUM(CASE WHEN YYMM = '2015-January' THEN QTY END) as [2015-January-QTY],
SUM(CASE WHEN YYMM = '2015-January' THEN QTY END) as [2015-January-AVG],
SUM(CASE WHEN YYMM = '2015-February' THEN QTY END) as [2015-February-QTY],
SUM(CASE WHEN YYMM = '2015-February' THEN QTY END) as [2015-February-AVG],
. . .
FROM #TABLE t
GROUP BY brand, sku;

Using two pivots and union all for the result set in your comment
select *, Remarks = 'QTY'
from (select brand, sku, qty, yymm from #table) src
pivot(sum(qty) for [yymm] in (
[2015-January], [2016-January], [2017-January] /* add other months here */
) ) as Pivoted
union all
select *, Remarks = 'AVG'
from (select brand, sku, yymm,amt/qty as avgpr from #table) src
pivot(sum(avgpr) for [yymm] in (
[2015-January], [2016-January], [2017-January] /* add other months here */
) ) as Pivoted
order by brand, sku, remarks desc;
rextester demo: http://rextester.com/ORPF1616
returns:
+----------+-----------+-------------------+------------------+------------------+---------+
| brand | sku | 2015-January | 2016-January | 2017-January | Remarks |
+----------+-----------+-------------------+------------------+------------------+---------+
| abott | 104590691 | NULL | NULL | NULL | QTY |
| abott | 104590691 | NULL | NULL | NULL | AVG |
| abott | 104591168 | 2,0000000000000 | NULL | NULL | QTY |
| abott | 104591168 | 100,0000000000000 | NULL | NULL | AVG |
| abott | 10467011A | NULL | NULL | NULL | QTY |
| abott | 10467011A | NULL | NULL | NULL | AVG |
| gsk | 104580709 | NULL | 2,0000000000000 | NULL | QTY |
| gsk | 104580709 | NULL | 79,5000000000000 | NULL | AVG |
| ranbaxcy | 104720038 | NULL | NULL | 2,0000000000000 | QTY |
| ranbaxcy | 104720038 | NULL | NULL | 84,5000000000000 | AVG |
+----------+-----------+-------------------+------------------+------------------+---------+

Related

SQL: SELECT all and Remove duplicated rows based on values from multiple columns

I have the following SQL table name 'orders':
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
| 111 | 1234 | 23 | 1 |
+--------------+------------+-------------------+------------+
And I'm trying to Select only the rows that the order_id and item_id are NOT the same (remove duplicates only if BOTH has the same value), I tried using "Group_By" as follows:
SELECT * FROM orders GROUP BY order_id,item_id
but this, will remove all duplicates of order_id, and all duplicated of item_id, here is the result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
+--------------+------------+-------------------+------------+
I've tried using 'DISTINCT' too, but I need to select all columns in the result.
here is the expected result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
+--------------+------------+-------------------+------------+
I hope its clear, Thank you.
You can use row_number():
select order_id, item_id, amount, commission
from (
select t.*, row_number() over(partition by order_id, item_id order by commission) rn
from mytable t
) t
where rn = 1
With your sample data, it is not easy to see exactly which partition and order by clause you are looking for, so you might need to adjust them to your exact use case.
Simply:
SELECT DISTINCT * FROM orders
SELECT DISTINCT should do what you want
SELECT DISTINCT order_id, item_id, amount, commission
FROM orders;
If you have more columns, but only care about these being duplicated, then you can use ROW_NUMBER():
SELECT o.*
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY order_id, item_id, amount, commission ORDER BY (SELECT NULL)) as seqnum
FROM orders o
) o
WHERE seqnum = 1;
You can try this.
create table MyTable
(order_id int
, item_id int
, amount int
, commission int)
insert into MyTable values
(111, 1234, 23, 1),
(222, 1234, 34, 2),
(111, 2345, 45, 3),
(111, 1234, 23, 1)
select distinct * from MyTable
Live db<>fiddle demo.

SQL Query SUM with conditions

I'm having some difficulties finding the good query I need, looking for help.
After multiple JOIN and a lot of WHERE, I have this columns and values :
+---------+----------+--------+-------+
| id_test | quantity | price | fee |
+---------+----------+--------+-------+
| "1-3" | 1 | 33.52 | 29.00 |
| "1-5" | 1 | 33.52 | 29.00 |
| "1-6" | 1 | 33.52 | 29.00 |
| "1-8" | 1 | 86.49 | 29.00 |
| "19-1" | 1 | 176.54 | 29.00 |
| "19-4" | 1 | 176.54 | 29.00 |
| "19-5" | 4 | 176.54 | 29.00 |
| "19-6" | 1 | 199.47 | 29.00 |
| "19-6" | 1 | 176.54 | 29.00 |
| "20-10" | 2 | 72.67 | 29.00 |
| "20-11" | 2 | 18.95 | 29.00 |
| "20-9" | 1 | 22.13 | 29.00 |
+---------+----------+--------+-------+
Each id_test object as a quantity, a price and a fee. I would like to SUM everything so to have a global price : (quantity*price)+fee
The thing is, and that's where I'm stuck, the fee has to be added only once by id_test. Yet, I have here two id_test "19-6".
If I SUM everything I have 1827.67 (price*quantity) + 348 (fee).
Since I have two "19-6", I need 1827.67 (price*quantity) + 319 (fee).
How would you do in SQL ?
Thanks
You can make use of Common Table Expressions (CTE)
Assuming your data in "data" table:
with total_fee as (
select distinct id_test, fee from data
),
total_price as (
select distinct id_test, sum(quantity) quantity, sum(price) price
from data
group by id_test
)
select sum(b.price*quantity) quantity, sum(a.fee) fee
from total_fee a
left join total_price b
on a.id_test = b.id_test
Only once by id_test means you have to take distinct id_test. If 29.00 is a fixed static value, you can take the distinct id_test and multiply it by 29.00
You can Insert the values of the above query to a temporary table ( #tempTable). Then your above output values will be temporary saved to a table
SELECT
DISTINCT(id_test) * 29.00 AS fee
FROM
#tempTable
select sum(quantity * price),
(select sum(fee)
from (select distinct(id_test), fee
from sales) as fees)
from sales;
I created a SQLFiddle to demonstrate.
You can use row_number()
SELECT SUM(price*quantity) + SUM(CASE WHEN rn = 1 THEN fee END) AS RESULT
FROM (SELECT *,ROW_NUMBER() OVER ( partition BY id_test) rn
FROM test) m
CHECK DEMO HERE
OUTPUT
Result
2146.67
Put id_test, (quantity * price) & fee in a temporary table.
WITH cte1 AS (
SELECT
id_test,
(quantity * price) AS total,
fee
FROM
table
)
Now add the total for every id
WITH cte2 AS (
SELECT SUM(total) + fee AS total_id
FROM cte1
GROUP BY id_test
)
Now find the total sum
SELECT SUM(total_id) AS SUM
FROM cte1
Just use aggregation:
select sum(price * quantity) + fee
from t
group by id_test, fee;
If you have different fees for the same test, you might want:
select sum(price * quantity) + avg(fee)
from t
group by id_test;

Select Latest 3 records

Using SQL Server 2014. I have data that lists a Unique Identifier, a Sale Data and a Sale Price. I would like to extract into a VIEW the last 3 sales for each unique Id.
Example of data:
+------+-----------+------------+-------------+
| ID | UNIQUE_ID | SaleDate | SalePrice |
+------+-----------+------------+-------------+
| 8210 | 1-5 | 2015-09-29 | 0 |
| 8211 | 1-6 | 2016-11-01 | 485672 |
| 8212 | 1-7 | 1994-06-24 | 120000 |
| 8213 | 1-1 | 1996-09-06 | 170000 |
| 8214 | 1-1 | 2000-01-28 | 265000 |
| 8215 | 1-1 | 2013-10-02 | 305000 |
| 8216 | 1-1 | 2015-11-20 | 1425000 |
| 8217 | 1-3 | 1994-01-12 | 1 |
| 8218 | 1-3 | 2001-04-30 | 1 |
| 8219 | 1-3 | 2004-09-30 | 0 |
+------+-----------+------------+-------------+
The result in the view would list each Unique ID and then 6 fields:
SaleDate1
SalePrice1
SaleDate2
SalePrice2
SaleDate3
SalePrice3
Any hints appreciated.
You can use row_number() :
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY UNIQUE_ID ORDER BY SaleDate DESC, SalePrice DESC) AS Seq
FROM table t
) t
WHERE Seq <= 3;
You can use a window function to filter data and then conditional aggregation to get the 6 columns you need:
declare #tmp table(ID int, UNIQUE_ID varchar(50), SaleDate date, SalePrice int)
insert into #tmp values
(8210, '1-5','2015-09-29', 0 )
,(8211, '1-6','2016-11-01', 485672 )
,(8212, '1-7','1994-06-24', 120000 )
,(8213, '1-1','1996-09-06', 170000 )
,(8214, '1-1','2000-01-28', 265000 )
,(8215, '1-1','2013-10-02', 305000 )
,(8216, '1-1','2015-11-20', 1425000)
,(8217, '1-3','1994-01-12', 1 )
,(8218, '1-3','2001-04-30', 1 )
,(8219, '1-3','2004-09-30', 0 )
SELECT t.UNIQUE_ID
,max(case when t.Seq = 1 then SaleDate else null end) as SaleDate1
,sum(case when t.Seq = 1 then SalePrice else null end) as SalePrice1
,max(case when t.Seq = 2 then SaleDate else null end) as SaleDate2
,sum(case when t.Seq = 2 then SalePrice else null end) as SalePrice2
,max(case when t.Seq = 3 then SaleDate else null end) as SaleDate3
,sum(case when t.Seq = 3 then SalePrice else null end) as SalePrice3
FROM (SELECT x.*,
ROW_NUMBER() OVER (PARTITION BY UNIQUE_ID
ORDER BY SaleDate DESC, SalePrice DESC) AS Seq
FROM #tmp x
) t
WHERE t.Seq < 4
group by t.UNIQUE_ID
Results:
The following query return the 3 most recent sold rows of each item
select * from
(
select UNIQUE_ID,SaleDate,SalePrice,rank() over (partition by UNIQUE_ID order by SaleDate desc) as rnk
from salestable
) where rnk<4

SQL group by on nested select statement

My data looks like this and here's google spread sheet
I want all the rows selected, along with an added extra row which is life of item (per item_code) in terms of weeks i.e. DATEDIFF(day, min(txn_date), txn_date)/7
I tried something like this:
SELECT txn_date, txn_qty, item_code,
( SELECT DATEDIFF(day, min(txn_date), txn_date)/7
FROM my_table
WHERE item_code like 'X6%'
GROUP BY item_code
) weeks_life
FROM my_table
WHERE item_code like 'X6%'
as for output, it should look like this:
I would use DATEDIFF and a MAX OVER clause for this.
declare #TEMP table (transaction_id int, txn_date date, txn_qty int, code char(5))
INSERT INTO #TEMP VALUES ( 1,'2016-09-20',1,'X6113')
INSERT INTO #TEMP VALUES ( 2,'2016-09-22',4,'X6113')
INSERT INTO #TEMP VALUES ( 3,'2016-11-08',7,'X6117')
INSERT INTO #TEMP VALUES ( 4,'2016-12-10',3,'X6117')
INSERT INTO #TEMP VALUES ( 5,'2016-12-22',1,'X6112')
INSERT INTO #TEMP VALUES ( 6,'2017-01-19',2,'X6118')
INSERT INTO #TEMP VALUES ( 7,'2017-02-11',4,'X6119')
INSERT INTO #TEMP VALUES ( 8,'2016-06-30',1,'X6117')
INSERT INTO #TEMP VALUES ( 9,'2016-08-03',5,'X6110')
INSERT INTO #TEMP VALUES (10,'2016-09-11',7,'X6118')
INSERT INTO #TEMP VALUES (11,'2016-10-29',1,'X6110')
INSERT INTO #TEMP VALUES (12,'2016-11-12',335,'X6113')
INSERT INTO #TEMP VALUES (13,'2017-01-06',1,'X6110')
INSERT INTO #TEMP VALUES (14,'2017-02-06',12,'X6113')
select transaction_id
,txn_date
,DATEDIFF(WEEK,MIN(txn_date) OVER(PARTITION BY code order by code),txn_date) as life_weeks
,txn_qty
,code
from #TEMP
References from MSDN
MAX (Transact-SQL)
OVER Clause (Transact-SQL)
DATEDIFF (Transact-SQL)
You can use window functions. I think this is what you want:
SELECT txn_date, txn_qty, item_code,
DATEDIFF(day,
MIN(txn_date) OVER (PARTITION BY item_code),
txn_date
)/7 as weeks_life
FROM my_table
WHERE item_code like 'X6%';
If you want to use an inline subquery like that, you could do this:
select
txn_date
, txn_qty
, item_code
, ceiling((
datediff(day, (
select min(txn_date)
from t i
where i.item_code = t.item_code
), txn_date)
) / 7.0) as weeks_life
from t
where item_code like 'X6%'
order by
item_code
, txn_date
cross apply() version:
select
txn_date
, txn_qty
, item_code
, ceiling((datediff(day, x.min_txn_date, txn_date)) / 7.0) as weeks_life
from t
cross apply (
select min_txn_date = min(txn_date)
from t i
where i.item_code = t.item_code
) as x
where item_code like 'X6%'
order by
item_code
, txn_date
inner join version:
select
t.txn_date
, t.txn_qty
, t.item_code
, ceiling((datediff(day, x.min_txn_date, t.txn_date)) / 7.0) as weeks_life
from t
inner join (
select min_txn_date = min(txn_date), item_code
from t i
group by item_code
) as x
on x.item_code = t.item_code
where t.item_code like 'X6%'
order by
t.item_code
, t.txn_date
rextester demo: http://rextester.com/BYTL99094
returns:
+------------+---------+-----------+------------+
| txn_date | txn_qty | item_code | weeks_life |
+------------+---------+-----------+------------+
| 2016-08-03 | 5 | X6110 | 0 |
| 2016-10-29 | 1 | X6110 | 13 |
| 2017-01-06 | 1 | X6110 | 23 |
| 2016-12-22 | 1 | X6112 | 0 |
| 2016-09-20 | 1 | X6113 | 0 |
| 2016-09-22 | 4 | X6113 | 1 |
| 2016-11-12 | 335 | X6113 | 8 |
| 2017-02-06 | 12 | X6113 | 20 |
| 2016-06-30 | 1 | X6117 | 0 |
| 2016-11-08 | 7 | X6117 | 19 |
| 2016-12-10 | 3 | X6117 | 24 |
| 2016-09-11 | 7 | X6118 | 0 |
| 2017-01-19 | 2 | X6118 | 19 |
| 2017-02-11 | 4 | X6119 | 0 |
+------------+---------+-----------+------------+

Getting the whole row from grouped result

Here's a sample database table :
| ID | ProductID | DateChanged | Price
| 1 | 12 | 2011-11-11 | 93
| 2 | 2 | 2011-11-12 | 12
| 3 | 3 | 2011-11-13 | 25
| 4 | 4 | 2011-11-14 | 17
| 5 | 12 | 2011-11-15 | 97
Basically, what I want to happen is get the latest price of grouped by ProductID.
The result should be like this :
| ID | ProductID | Price
| 2 | 2 | 12
| 3 | 3 | 25
| 4 | 4 | 17
| 5 | 12 | 97
If you notice, the first row is not there because there is a new price for ProductID 12 which is the row of ID 5.
Basically, it should be something like get ID,ProductID and Price grouped by productID where DateChanged is the latest.
SELECT ID, ProductId, Price
FROM
(
SELECT ID, ProductId, Price
, ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY DateChanged DESC) AS rowNumber
FROM yourTable
) AS t
WHERE t.rowNumber = 1
SELECT ID, ProductID,DateChanged, Price
FROM myTable
WHERE ID IN
(
SELECT MAX(ID)
FROM myTable
GROUP BY ProductID
)
select a.id, a.productid, a.price
from mytable a,
(select productid, max(datechanged) as maxdatechanged
from mytable
group by productid) as b
where a.productid = b.productid and a.datechanged = b.maxdatechanged
SELECT ID, ProductId, Price
from myTable A
where DateChanged >= all
(select DateChanged
from myTable B
where B.ID = A.ID);