Pivot a table with multiple values - sql

I'm trying to pivot an access table with multiple result values like the first table in the image below. Anyone has an idea on how to get a result like in the second table?

You canĀ“t use multiple agregations with Pivot table, but there is an easy solution for your problem
By creating two pivot tables and joining both tables separed by Price and Weight is posible.
/*PRICE*/
SELECT MONTH
,ITEM
,STORE
,MANAGER
,[TY] AS TY_PRICE
,[LY] AS LY_PRICE
,[PY] AS PY_PRICE
INTO ##TMP_PRICE
FROM(
select MONTH
,ITEM
,STORE
,MANAGER
,TYPE
,SUM(PRICE) AS PRICE
FROM TABLE_X
GROUP BY MONTH
,ITEM
,STORE
,MANAGER
,TYPE) AS TMP
PIVOT(
MAX(PRICE)
FOR TYPE IN([TY],[LY],[PY])
) AS PVT
/*WEIGHT*/
SELECT MONTH
,ITEM
,STORE
,MANAGER
,[TY] AS TY_WEIGHT
,[LY] AS LY_WEIGHT
,[PY] AS PY_WEIGHT
INTO ##TMP_WEIGHT
FROM(
select MONTH
,ITEM
,STORE
,MANAGER
,TYPE
,SUM(WEIGHT) AS WEIGHT
FROM TABLE_X
GROUP BY MONTH
,ITEM
,STORE
,MANAGER
,TYPE) AS TMP
PIVOT(
MAX(WEIGHT)
FOR TYPE IN([TY],[LY],[PY])
) AS PVT
After both tables are created join them
SELECT ISNULL(TP.month,TW.month) as MONTH
,ISNULL(TP.ITEM,TW.ITEM) AS ITEM
,ISNULL(TP.STORE,TW.STORE) AS STORE
,ISNULL(TP.MANAGER,TW.MANAGER) AS MANAGER
,TY_WEIGHT
,LY_WEIGHT
,PY_WEIGHT
,TY_PRICE
,LY_PRICE
,PY_PRICE
FROM ##TMP_PRICE TP
FULL JOIN ##TMP_WEIGHT TW ON (TP.MONTH = TW.MONTH AND TP.ITEM = TW.ITEM
AND TP.STORE = TW.ITEM AND TP.MANAGER = TW.MANAGER)

Because you can't use pivot queries as sub queries in a query in Microsoft Access, you would have to save two pivot queries to separate Microsoft Access query objects:
Pivot_Price
TRANSFORM Sum([Price]) AS SumPrice
SELECT [month], item, store, manager
FROM Table1
GROUP BY [month], item, store, manager
PIVOT [type];
Pivot_Weight
TRANSFORM Sum([Weight]) AS SumWeight
SELECT [month], item, store, manager
FROM Table1
GROUP BY [month], item, store, manager
PIVOT [type];
And then join them in a third query:
SELECT
PP.Month,
PP.item,
PP.store,
PP.manager,
PP.TY AS [TY-Price],
PP.LY AS [LY-Price],
PP.PY AS [PY-Price],
PW.TY AS [TY-Weight],
PW.LY AS [LY-Weight],
PW.PY AS [PY-Weight]
FROM
Pivot_Price AS PP INNER JOIN Pivot_Weight AS PW
ON
PP.month = PW.month AND
PP.item = PW.item AND
PP.store = PW.store AND
PP.manager = PW.manager

Related

Iterate through SQL select query to get more related data

I would like to generate an excel report based on a SQL stored procedure.
Therefore i have to 'fill' few columns on this report. Some are related to (let's say) the Order table, others are related to the Product table and so on.
On the excel report i need to see the columns: Order no, Order value, Product 1 name, Product 2 name, Product 1 value, Product 2 value (there are max 2 products on each order).
The Product table is linked to the Order table.
I know this kind of listing it's a bit confusing, but that is what i wanna get.
Atm i have written the sequence:
SELECT
order.OrderNo,
order.OrderValue,
product.Name,
product.Value
From ORDER AS order
lEFT outer join PRODUCT as product on order.OrderId = product.OrderId
The query works but i only get data for the first product in each order. Is there a way to select data from all products specific to each order?
You can use row_number() and conditional aggregation:
SELECT op.OrderNo, op.OrderValue,
MAX(CASE WHEN seqnum = 1 THEN Name END) as Name_1,
MAX(CASE WHEN seqnum = 1 THEN Value END) as Value_1,
MAX(CASE WHEN seqnum = 2 THEN Name END) as Name_2,
MAX(CASE WHEN seqnum = 2 THEN Value END) as Value_2
FROM (SELECT o.OrderNo, o.OrderValue, p.Name, p.Value,
ROW_NUMBER() OVER (PARTITION BY o.OrderNo ORDER BY p.Value DESC) as seqnum
FROM ORDER o LEFT JOIN
PRODUCT p
ON o.OrderId = p.OrderId
) op
GROUP BY op.OrderNo, op.OrderValue;
If your DBMS supports it, you can use a PIVOT for this, for example:
declare #order table(
OrderId int,
OrderNo varchar(10),
OrderValue decimal(10,2)
)
declare #product table(
OrderId int,
[Name] varchar(20),
[Value] decimal(10,2)
)
insert into #order values
(1,'ORD001',436.45),
(2,'ORD002',964.33),
(3,'ORD003',1265.98)
insert into #product values
(1,'Widget',195.45),
(1,'Doohickey',241.00),
(2,'Widget',195.45),
(2,'Thingy',397.99),
(2,'Doofer',370.89),
(3,'Widget',195.45),
(3,'Thingy',397.99),
(3,'Foobar',415.78),
(3,'Whatchamacallit',256.76)
select
OrderNo,
OrderValue,
[Widget],
[Doohickey],
[Thingy],
[Doofer],
[Foobar],
[Whatchamacallit]
from
(
select
o.OrderNo,
o.OrderValue,
p.[Name],
p.[Value]
from #order o
left join #product p on o.OrderId = p.OrderId
) SourceTable
pivot
(
sum([Value])
for [Name] in ([Widget], [Doohickey], [Thingy], [Doofer], [Foobar], [Whatchamacallit])
) as PivotTable
gives you this result:
However, this does require you to know all the distinct values of Product.Name which could appear and to include them in your query. The upside is you can perform further aggregation on the resultant values, per Product.

Sql select distinct row by a columns highest value

I am having an issue trying to select one row per city name. This is the following collection I am getting:
This is my query so far:
select pl.PlaceId,
pl.Name,
pop.NumberOfPeople,
pop.Year
from dbo.Places pl
inner join dbo.Populations pop
on pop.PlaceId = pl.PlaceId
where pop.NumberOfPeople >= 1000
and pop.NumberOfPeople <= 99999
I am trying to get it to where it only selects a city one time, but uses the most recent date. So in the above picture, I would only see Abbeville for 2016 and not 2015. I believe I need to do either a group by or do a sub query to flatten the results. If anybody has any advice on how I can handle this, it will be greatly appreciated.
Assuming you are using SQLSERVER,you can use Rownumber
;with cte
as
(select pl.PlaceId,
pl.Name,
pop.NumberOfPeople,
pop.Year,
row_number() over(partition by pl.Name order by year desc) as rownum
from dbo.Places pl
inner join dbo.Populations pop
on pop.PlaceId = pl.PlaceId
where pop.NumberOfPeople >= 1000
and pop.NumberOfPeople <= 99999
)
select * from cte where rownum=1
The following query serves the purpose.
CREATE TABLE #TEMP_TEST
(
PlaceId INT,
Name VARCHAR(50),
NumberOfPeople INT,
YEAR INT
)
INSERT INTO #TEMP_TEST
SELECT 1,'Abbeville',2603,2016
UNION
SELECT 5,'Alabester',32948,2016
UNION
SELECT 9,'Aubum',63118,2016
UNION
SELECT 1,'Abbeville',2402,2015
UNION
SELECT 5,'Alabester',67902,2017
SELECT PlaceId, Name, NumberOfPeople, YEAR FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY PlaceId ORDER BY YEAR DESC) RNO,
PlaceId, Name, NumberOfPeople, YEAR
FROM #TEMP_TEST
)T
WHERE RNO = 1
DROP TABLE #TEMP_TEST

Multiple Aggregates Pivot Table

I have a few columns of data I am wanting to put into a Pivot table. This is what the data currently looks like once the following query is ran:
SELECT
t.clinic,
t.fiscal_year,
t.total,
n.new_pats,
(t.total - n.new_pats) AS active
FROM (SELECT DISTINCT
clinic,
fiscal_year,
COUNT(DISTINCT patient_id) AS total
FROM transactions t
JOIN period p
ON (t.date_entered BETWEEN p.period_start AND p.period_end)
GROUP BY clinic, fiscal_year) t
JOIN (SELECT DISTINCT
clinic,
per.fiscal_year,
COUNT(DISTINCT patient_id) AS new_pats
FROM patient pat
JOIN period per
ON (pat.first_visit_date BETWEEN per.period_start AND per.period_end)
GROUP BY clinic, fiscal_year) n
ON (t.clinic = n.clinic AND t.fiscal_year = n.fiscal_year)
And I would like the data to be broken up by the three aggregates in their own rows and the years being the columns where it looks something like this:
This could also be broken into 3 separate columns per aggregate per year, but this is what I'm ideally aiming for. I haven't put together a Pivot table within SQL before and am at a loss. Is there a better way to go about formatting this data in the desired way?
You could try this
SELECT clinic,
aggregate_field,
ISNULL([2009],0) AS [2009],
ISNULL([2010],0) AS [2010],
ISNULL([2016],0) AS [2016],
ISNULL([2017],0) AS [2017]
FROM (SELECT clinic,
fiscal_year,
aggregate_field,
value
FROM (
------- This part is your original query -----------------
SELECT t.clinic,
t.fiscal_year,
t.total,
n.new_pats,
( t.total - n.new_pats ) AS active
FROM (SELECT DISTINCT clinic,
fiscal_year,
Count(DISTINCT patient_id) AS total
FROM transactions t
JOIN period p
ON ( t.date_entered BETWEEN
p.period_start AND p.period_end )
GROUP BY clinic, fiscal_year) t
JOIN (SELECT DISTINCT clinic,
per.fiscal_year,
Count(DISTINCT patient_id) AS new_pats
FROM patient pat
JOIN period per
ON ( pat.first_visit_date BETWEEN
per.period_start AND per.period_end )
GROUP BY clinic, fiscal_year) n
ON ( t.clinic = n.clinic
AND t.fiscal_year = n.fiscal_year )
-------------------------------------------------------------
)
unpivot_source
UNPIVOT ( value
FOR aggregate_field IN (total,
new_pats,
active) ) unpivot_result) AS
pivot_source
PIVOT ( Max(value)
FOR fiscal_year IN ([2009],
[2010],
[2016],
[2017]) ) AS pivot_result
ORDER BY clinic, aggregate_field DESC
I've create a demo here http://rextester.com/MFHWV68715.
Try this:
CREATE TABLE #ActualData
(
Clinic varchar(50),
fiscal_year int,
total int,
new_pats int,
active int
)
INSERT INTO #ActualData VALUES ('A', 2016, 3538,1787,1751), ('A', 2017, 1218,373,845), ('B', 2009, 1,2,3), ('B', 2010, 1,2,3)
SELECT * FROM #ActualData ad
;WITH temps AS
(
SELECT Clinic, fiscal_year, aggregateF, Number FROM
(
SELECT ad.Clinic, ad.fiscal_year, ad.total,ad.new_pats,ad.active FROM #ActualData ad
) src
UNPIVOT
(
Number FOR aggregateF IN (total,new_pats ,active)
) unpvt
)
SELECT Clinic, aggregateF, [2009],[2010],[2016],[2017]
FROM
(
SELECT t.Clinic, t.aggregateF, t.fiscal_year, t.Number FROM temps t
) src
PIVOT
(
MIN (Number) FOR fiscal_year IN ([2009],[2010],[2016],[2017])
) pvt
ORDER BY pvt.Clinic, pvt.aggregateF DESC
DROP TABLE #ActualData

Average of last X orders

I need to find the average amount of the last X orders, per customer. My data is structured as such:
Customer ID
Total Amount
Date
I tried partitionning by Customer ID, then Ordering by date, but I can't find the average of the TOP X.
Another option is a cte and Row_Number(). The following will give you the average order by customer.
Declare #YourTable table (CustID int,OrderDate Date,OrderAmount int)
Insert Into #YourTable values
(1,'2016-01-18',2500),
(1,'2016-02-13',5000),
(1,'2016-03-31',3000),
(2,'2016-03-18',1800),
(2,'2016-04-13',2200),
(2,'2016-05-31',2500)
;with cteBase as (
Select *
,RowNr=Row_Number() over (Partition By CustID Order By OrderDate Desc)
From #YourTable
)
Select CustID
,AvgOrder = avg(OrderAmount)
From cteBase
Where RowNr<=2
Group By CustID
Returns
CustID AvgOrder
1 4000
2 2350
Use ROW_NUMBER window function
select [Customer ID],Avg([Total Amount]) as avg_amt
(
select row_number()over(partition by [Customer ID] order by [Date] desc) rn, *
from yourtable
) A
Where Rn <= 5 --change it based on requirement
Group by [Customer ID]
Looks like current table is transaction table, so you may have a separate table called Customer where [Customer ID] is unique then you can use this approach also
SELECT c.[Customer ID],
Avg([Total Amount])
FROM customers c
CROSS apply (SELECT TOP 5 [Total Amount]
FROM yourtable y
WHERE c.[Customer ID] = y.[Customer ID]
ORDER BY [Date] DESC) cs
GROUP BY [Customer ID]
The above query can be altered with OUTER APPLY to get list of all customers even though he did not make any transaction with the avg Total Amount zero.
One method uses row_number():
select customerid, avg(totalamount)
from (select t.*,
row_number() over (partition by customerid order by date desc) as seqnum
from t
) t
where seqnum <= x;

How to run distinct and Sum in one query in sql server 2008 R2

I have a table #1 as shown in image attached. First i want to sum all quantity of all distinct id. Then want to show number of id that have same quantity.
Use SUM and COUNT:
SELECT
COUNT(*) AS totalId,
qty
FROM (
SELECT
id, SUM(qty) AS qty
FROM tbl
GROUP BY id
)t
GROUP BY qty
ONLINE DEMO
Try this one after creating a temporary table
create table #Temp
(
id int,
qty int
)
Insert Into #Temp
SELECT id, SUM(qty)
FROM yourTable
group by id
SELECT * FROM #Temp
SELECT Count(id) , qty
FROM #Temp
GROUP BY qty
ORDER BY qty DESC
to show the sum of all quantities of all distinct id:
SELECT id,SUM(qty) FROM table GROUP BY id;
to show number of id that have same quantity
SELECT count(id),quantity FROM (SELECT id,SUM(qty) AS quantity FROM table GROUP BY id) GROUP BY quantity