SQL pivot / VLOOKUP query - sql

I have the following simplified table so each stock code has
multiple warehouses and I want to "pivot" it so I can get
the cost for each warehouse just in 1 line for the stockcode
Stockcode | Warehouse | Cost
A100 WH 100$
A100 ZZ 200$
What I would like to have is the following.
So only 1 stockcode line with the warehouse costs pivoted.
Stockcode | Cost WH | Cost ZZ
A100 100$ 200$
I currently did it by exporting it to excell, creating a new sheet and add a new column to the sheet using a VLOOKUP to the main table and add the ZZ cost like that.
How would you turn this around in SQL? I am a bit stuck with understanding how to transform this tabular data in such a way.

Here's how to do this query using PIVOT:
select Stockcode, [WH] as CostWH, [ZZ] as CostZZ
from (
select Stockcode, Warehouse, Cost
from MyTable
) p
pivot (
MAX(Cost)
for Warehouse in ([WH], [ZZ])
) as pvt
order by Stockcode;
Test it on SQLFiddle

I think (Stockcode,Warehouse) is unique in your table.
And I think you can use PIVOT like it
SELECT Stockcode,[WH],[ZZ]
FROM TestData PIVOT(MAX(Cost) FOR Warehouse IN([WH],[ZZ])) p
And use dynamic query for generate query for all the warehouses
DECLARE #Warehouses varchar(500)=''
SELECT #Warehouses+=CONCAT(',[',Warehouse,']')
FROM
(
SELECT DISTINCT Warehouse
FROM TestData
) q
ORDER BY Warehouse
SET #Warehouses=STUFF(#Warehouses,1,1,'')
PRINT #Warehouses
DECLARE #query varchar(2000)=CONCAT('SELECT Stockcode,',#Warehouses,'
FROM TestData PIVOT(MAX(Cost) FOR Warehouse IN(',#Warehouses,')) p')
PRINT #query
EXEC(#query)
My another answer similar to it - Display count results of requests with results of jobs horizontally and locations vertically 3 tables

However, you could also do that by using simple case expression if you don't want to go pivot or dynamic pivot
SELECT
Stockcode ,
MAX( CASE (Warehouse) WHEN 'WH' THEN Cost END)[Cost WH],
MAX(CASE (Warehouse) WHEN 'ZZ' THEN Cost END) [Cost ZZ]
FROM <table> GROUP BY Stockcode
Dynamic pivot way :
DECLARE #Col nvarchar(max), #query nvarchar(max)
select #Col = STUFF(
(SELECT ',' +QUOTENAME(Warehouse) FROM <table> FOR XML PATH('')),
1, 1, ''
)
SELECT #query = N'select Stockcode, [WH] as CostWH, [ZZ] as CostZZ
from (
select Stockcode, Warehouse, Cost
from <table>) p
pivot (max(Cost) for Warehouse in ('+#Col+')
) as pvt order by Stockcode;'
exec(#query)
Result :
Stockcode Cost WH Cost ZZ
A100 100$ 200$

Related

Find similar sales orders in SQL

This is my first post.
I work at a manufacturing company and most of the products we are making are custom made.
We believe we can find some commonalities in the products we sale.
To do this, we need to analyze sales orders and compare them to all the sales orders in our system to find identical ones.
Here's an example in form of a SQL result:
etc...
+------------------------------+
| OrderId ProductCode Qty |
+------------------------------+
| SS1234 Widget1 1 |
| SS1234 Widget2 3 |
| SS1234 Widget3 1 |
+------------------------------+
I would like to find orders similar to SS1234, ie orders with the same products (widget1, widget2 and widget3) and the same quantities.
How do I do this in SQL Server 2008R2?
Thanks for your help!
Raf
I won't be able to test this before I go to bed for the evening. This is an overly verbose approach, but I wanted to grind this out as quickly as possible so I tried to use structure / syntax that I know well, instead of trying to write more concise, efficient code that would require I lean on the documentation. Basically, we're counting the number of items in each order, selecting a pair of order ids every time we find two matching line items, then we count how many times an exact pair of order IDs appears. Use inner joins to filter out pairs that matched fewer times than there are products in the order.
WITH
ProductCounts AS (
SELECT COUNT(OrderID) AS ProductCodesInOrder, OrderID
FROM Table
GROUP BY OrderID
), MatchingLineItems AS (
SELECT A.OrderID AS FirstOrderID, B.OrderID AS SecondOrderID
FROM Table AS A
INNER JOIN Table AS B
ON A.ProductCode = B.ProductCode AND A.Qty = B.Qty
ORDER BY FirstOrderID, SecondOrderID
), MatchTotals AS (
SELECT
COUNT(FirstOrderID) AS Matches, FirstOrderID, SecondOrderID
FROM MatchingLineItems
GROUP BY FirstOrderID, SecondOrderID
), FirstMatches AS (
SELECT MatchTotals.FirstOrderID, MatchTotals.SecondOrderID, MatchTotals.Matches
FROM MatchTotals
INNER JOIN ProductCounts
ON MatchTotals.FirstOrderID = ProductCounts.OrderID
WHERE MatchTotals.Matches = ProductCounts.ProductCodesInOrder
)
SELECT FirstMatches.FirstOrderID, FirstMatches.SecondOrderID
FROM FirstMatches
INNER JOIN ProductCounts
ON FirstMatches.SecondOrderID = ProductCounts.OrderID
WHERE FirstMatches.Matches = ProductCounts.ProductCodesInOrder
Setup:
CREATE TABLE #ord (
OrderId VARCHAR(20),
ProductCode VARCHAR(40),
qty int
)
INSERT INTO #ord (OrderId, ProductCode, Qty)
VALUES
('SS1234','Widget1',1)
,('SS1234','Widget2',3)
,('SS1234','Widget3',1)
,('SS1234a','Widget1',1)
,('SS1234a','Widget2',3)
,('SS1234a','Widget3',1)
,('xSS1234','Widget1',1)
,('xSS1234','Widget2',3)
,('xSS1234','Widget3',1)
,('xSS1234','Widget4',1)
,('ySS1234','Widget1',10)
,('ySS1234','Widget2',3)
,('ySS1234','Widget3',1)
,('zSS1234','Widget2',3)
,('zSS1234','Widget3',1)
;
Query:
with CTE as (
select distinct
o.OrderID, ca.ProductString, ca.QtyString
from #ord o
cross apply (
SELECT
STUFF((
SELECT
', ' + o2.ProductCode
FROM #ord o2
WHERE o.OrderID = o2.OrderID
ORDER BY o2.ProductCode
FOR XML PATH ('')
)
, 1, 1, '')
, STUFF((
SELECT
', ' + cast(o2.Qty as varchar)
FROM #ord o2
WHERE o.OrderID = o2.OrderID
ORDER BY o2.ProductCode
FOR XML PATH ('')
)
, 1, 1, '')
) ca (ProductString, QtyString)
)
select
ProductString, QtyString, count(*) Num_Orders
from CTE
group by
ProductString, QtyString
having
count(*) > 1
order by
Num_Orders DESC
, ProductString
Result:
ProductString QtyString Num_Orders
Widget1, Widget2, Widget3 1, 3, 1 2
See: http://rextester.com/DJEN59714

How to make this query for this scenario in SQL SERVER?

I have two table called TransactionItem, ItemRelation, below are my queries
select sum(dqty) as QUANTITY FROM [HQMatajer].[dbo].[TransactionItem]
where ItemLookupCode='306519P0001' group by Tyear, Tmonth
select
ItemLookupCode, convert(INT,SUBSTRING(ItemLookupCode,8,4)) as SIZE from
[HQMatajer].[dbo].[ItemRelation] where ItemLookupCode='319348P0001' or
ChildItemLookupCode1='319348P0001' or ChildItemLookupCode2='319348P0001' or
ChildItemLookupCode='319348P0001'
calculations
Result of my query of two tables
As you can see the result on image, now I just want to divide all the values of quantity from first result by size from second result. For example
QUANTITY
6/48
64/48
167/48
31/48
2367/48
1664/48
8/48
How to do it in a single query?.
How about this:
SELECT QUANTITY / SIZE QUANTITY FROM (
select sum(dqty) as QUANTITY
FROM [HQMatajer].[dbo].[TransactionItem]
where ItemLookupCode='306519P0001'
group by Tyear, Tmonth
) A
CROSS JOIN (
select ItemLookupCode, convert(INT,SUBSTRING(ItemLookupCode,8,4)) as SIZE
from [HQMatajer].[dbo].[ItemRelation]
where
ItemLookupCode='319348P0001'
or ChildItemLookupCode1='319348P0001'
or ChildItemLookupCode2='319348P0001'
or ChildItemLookupCode='319348P0001'
) B
You can calculate size and put it in a variable. Then you can use the same variable for division in the second query.
declare #size int;
set #size= (Select top 1 convert(INT,SUBSTRING(ItemLookupCode,8,4)) as SIZE from [HQMatajer].[dbo].[ItemRelation]
where ItemLookupCode='319348P0001' or ChildItemLookupCode1='319348P0001' or ChildItemLookupCode2='319348P0001' or ChildItemLookupCode='319348P0001')
select sum(dqty)/#size as QUANTITY FROM [HQMatajer].[dbo].[TransactionItem]
where ItemLookupCode='306519P0001'
group by Tyear, Tmonth
CREATE TABLE #Tbl1(Qty INT)
CREATE TABLE #Tbl2(Size INT)
INSERT INTO #Tbl1(Qty)
SELECT 6 UNION ALL
SELECT 64 UNION ALL
SELECT 167
INSERT INTO #Tbl2(Size)
SELECT 48
SELECT Qty / (SELECT Size FROM #Tbl2)
FROM #Tbl1
Use the variable to store the sum. then we can use this variable while dividing in the next query.
Declare #QUANTITY float;
select #QUANTITY=sum(dqty)
FROM [HQMatajer].[dbo].[TransactionItem]
where ItemLookupCode='306519P0001'
group by Tyear, Tmonth
select ItemLookupCode
,convert(INT,SUBSTRING(ItemLookupCode,8,4)) as SIZE, convert(INT,SUBSTRING(ItemLookupCode,8,4))/#QUANTITY
from [HQMatajer].[dbo].[ItemRelation]
where (ItemLookupCode='319348P0001' or ChildItemLookupCode1='319348P0001' or ChildItemLookupCode2='319348P0001' or ChildItemLookupCode='319348P0001')

How to pivot 2 columns into one row

I am trying to pivot a table that has both Units and Base Units for a certain product. I am trying to make my end result one line only.
Product Unit BaseUnit
Gas MMcf Mcf
Oil Mbbl bbl
Water Mgal gal
My output table should look like this:
GasUnit GasBaseUnit OilUnit OilBaseUnit WaterUnit WaterBaseUnit
MMcf Mcf Mbbl bbl Mgal gal
I have done a pivot table before but never had to pivot 2 columns at the same time and move them into one row.
Here is what I have so far. Can I use 2 aggregates? I am not sure how to approach this.
SELECT *
from
(
select Product, Unit, BaseUnit
from t
) x
pivot
(
sum(BaseUnit)
for Product in ([Gas], [Oil], [Water])
) p
With static data this will get your output table
WITH prep AS (
SELECT Product + ' Unit' TYPE, Unit Value
FROM utilities
UNION ALL
SELECT Product + ' BaseUnit' TYPE, BaseUnit Value
FROM utilities
)
SELECT [Gas Unit], [Gas BaseUnit]
, [Oil Unit], [Oil BaseUnit]
, [Water Unit], [Water BaseUnit]
FROM (SELECT TYPE, Value
FROM prep
) a
PIVOT
(MAX(Value)
FOR TYPE IN ([Gas Unit], [Gas BaseUnit]
, [Oil Unit], [Oil BaseUnit]
, [Water Unit], [Water BaseUnit])
) pvt
Demo: SQLFiddle
With dynamic data you'll need to use a dynamic query to create the PIVOT columns list

SQL Server query troubles, many-many relationship

Not sure how to word this question in one line, apologies for the title...
I have 3 tables in my database, for example:
Shop
Item
ShopStock
Shop and Item have a many-many relationship and so the ShopStock table links them.
The fields in ShopStock are:
ID
ShopID
ItemID
CurrentStock
I want to list the items, showing how much stock each shop has, but I'm having trouble with the SQL. Something like this:
ITEM TESCO STOCK ASDA STOCK SAINSBURY STOCK
Apples 5 20 74
Pears 1000 32 250
How do I build the SQL query to display the data like this?
This would be easier to list as item,shop,currentstock in multiple rows. As is, unless you know the number of shops, you're going to need to use dynamic sql for this. If you know the number of potential shops, you can use PIVOT to return your results.
Something like this assuming you had 2 shops (shop1 and shop2):
select item_name, [Shop1], [Shop2]
from
(
select item_name, shop_name, currentstock
from item i
join shopstock ss on i.item_id = ss.item_id
join shop s on s.shop_id = ss.shop_id
) x
pivot
(
max(currentstock)
for shop_name in ([Shop1],[Shop2])
) p
SQL Fiddle Demo
Here is the dynamic sql approach as I suspect you don't know the number of possible shops:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = stuff((select distinct ',' + quotename(shop_name)
from shop
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select item_name,' + #cols + '
from
(
select item_name, shop_name, currentstock
from item i
join shopstock ss on i.item_id = ss.item_id
join shop s on s.shop_id = ss.shop_id
) x
pivot
(
max(currentstock)
for shop_name in (' + #cols + ')
) p '
execute(#query)
SQL Fiddle Demo
You might need to add JOINS to get specific names, but this is the idea you're after:
SELECT ItemID
, MAX(CASE WHEN ShopID = 'Tesco' THEN CurrentStock ELSE 0 END)'Tesco Stock'
, MAX(CASE WHEN ShopID = 'ASDA' THEN CurrentStock ELSE 0 END)'ASDA Stock'
, MAX(CASE WHEN ShopID = 'Sainsbury' THEN CurrentStock ELSE 0 END)'SainsburyStock'
FROM ShopStock
GROUP BY ItemID
Assuming one entry per item per shopID. If there are multiples then you would have to SUM() them, but the idea is the same.

SQL Pivot Command

I am looking for some help on designing a simple pivot so that I can link it into other parts of my queries.
My data is like this
Items Table
Below is my table if I run Select * from items
ITEM Weight
12345 10
12345 11
654321 50
654321 20
654321 100
There are hundreds of Items in this table but each item code will only ever have
maximum of 3 weight records each.
I want the desired output
ITEM Weight_1 Weight_2 Weight_3
12345 10 11 null
654321 50 20 100
Would appreciate any suggestions,
I have played around with pivots but each subsequent item puts the weights into weight 4,5,6,7,etc
instead of starting at weight1 for each item.
Thanks
Update
Below is what I have used so far,
SELECT r.*
FROM (SELECT 'weight' + CAST(Row_number() OVER (ORDER BY regtime ASC)AS
VARCHAR(10))
line,
id,
weight
FROM items it) AS o PIVOT(MIN([weight]) FOR line IN (weight1, weight2,
weight3)) AS r
You were almost there! You were only missing the PARTITION BY clause in OVER:
SELECT r.*
FROM (SELECT 'weight' + CAST(Row_number() OVER (PARTITION BY id ORDER BY
regtime ASC)
AS
VARCHAR(10)) line,
id,
weight
FROM items it) AS o PIVOT(MIN([weight]) FOR line IN (weight1, weight2,
weight3)) AS r
When you PARTITION BY by ID, the row numbers are reset for each different ID.
Update
You do not need dynamic pivot, since you will always have 3 weights. But, if you ever need dynamic number of columns, take a look at some of the examples here:
SQL Server PIVOT perhaps?
Pivot data in T-SQL
How do I build a summary by joining to a single table with SQL Server?
You will need a value to form the columns which I do with row_number. The outcome is what you want. The only negative that I have against PIVOT is that you need to know how many columns in advance. I use a similar method, but build up the select as dynamic SQL and can then insert my columns.
EDIT: updated to show columns as weight1, weight2, etc.
create table #temp (Item int, Weight int)
insert into #temp (Item, Weight)
Values (12345, 10),
(12345, 11),
(654321, 50),
(654321, 20),
(654321, 200)
SELECT *
FROM (SELECT Item,
Weight,
'weight' + cast(Row_number()
OVER (partition by Item order by item) as varchar(10)) as seq
FROM #temp) as Src
PIVOT ( MAX(Weight) FOR Seq IN ([Weight1], [Weight2], [Weight3]) ) as PVT
MySQL
Whenever you need a pivot, use group_concat it will output a CSV list of the values you need.
Once you get used to working with it, it's a great tool.
SELECT item, GROUP_CONCAT(weight) as weights FROM table1
GROUP BY item
See: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
TSQL aka SQL-server
Many many questions on this because T-SQL supports a pivot keyword.
See:
Transact SQL Query-Pivot-SQL
Pivot data in T-SQL