I have a table of product stock (ProductStockTemp):
ProductStockId | ProductCode | ProductName | ProductStockClosingBalance
-------------------------------------------------------------------------
1 | A1 | A2 | 200
2 | B1 | B2 | 0
3 | A1 | A2 | 100
4 | C1 | C2 | -400
5 | B1 | B2 | 700
6 | C1 | C2 | 0
7 | D1 | D2 | 0
8 | D1 | D2 | 0
I would like to create a table like that
ProductCode | ProductName | TypeA | TypeB
------------------------------------------
A1 | A2 | 200 | 100
B1 | B2 | 0 | 700
C1 | C2 | -400 | 0
D1 | D2 | 0 | 0
It means the first product record will be the product type A and the latest record will be the product type B. I think i have to select the latest product record first. However, the problem arises when the product appears one time then it will choose this record. In this case, I want the product type B will be 0 since the first will be the product type A and the latest will be the type B. My query to select the latest product record in the table
select
p.ProductCode,
p.ProductName,
p.ProductStockClosingBalance
from
ProductStockTemp p
where
p.ProductStockId = (select MAX(q.ProductStockId)
from ProductStockTemp q
where p.ProductCode = q.ProductCode)
order by
p.ProductCode
I'm really stuck right now!
Most obvius in my opinion for this case:
;with cteStock as
(
select
p.ProductCode,
p.ProductName,
p.ProductStockClosingBalance,
row_number() over(partition by p.ProductCode, p.ProductName order by p.ProductStockId asc) rn_asc,
row_number() over(partition by p.ProductCode, p.ProductName order by p.ProductStockId desc) rn_desc,
from ProductStockTemp p
)
select
s.ProductCode, s.ProductName,
sum(case when s.rn_asc = 1 then s.ProductStockClosingBalance else 0) as TypeA,
sum(case when s.rn_desc = 1 and s.rn_asc != s.rn_desc then s.ProductStockClosingBalance else 0) as TypeB
from cteStock s
group by s.ProductCode, s.ProductName
but it's no good to have such a denormalized table with no info about period and names stored right here.
s.rn_asc != s.rn_desc - for the case when there is only one row.
Not sure about default zero but it's up to you and your task definition.
I suggest you to learn SQL GROUP BY Statement
SELECT ProductCode, ProductName, min(ProductStockClosingBalance) as TypeA, Max(ProductStockClosingBalance) as TypeB
FROM ProductStockTemp p
GROUP BY ProductCode, ProductName
This query does what you want.
declare #prod table(ProductStockId int, ProductCode varchar(10), ProductName varchar(10), ProductStockClosingBalance int)
insert #prod values
( 1 , 'A1' , 'A2' , 200),
( 2 , 'B1' , 'B2' , 0),
( 3 , 'A1' , 'A2' , 100),
( 4 , 'C1' , 'C2' , -400),
( 5 , 'B1' , 'B2' , 700),
( 6 , 'C1' , 'C2' , 0),
( 7 , 'D1' , 'D2' , 0),
( 8 , 'D1' , 'D2' , 0 )
;with prod as(
select *
,ROW_NUMBER() over(partition by productcode order by ProductStockId) rn1
,ROW_NUMBER() over(partition by productcode order by ProductStockId desc) rn2
from #prod
)
select productcode,ProductName,
(select ProductStockClosingBalance from prod p1 where p.productcode=p1.ProductCode and p1.rn1=1) TypeA,
ProductStockClosingBalance typeB
from prod p
where rn2=1
Results:
productcode ProductName TypeA typeB
A1 A2 200 100
B1 B2 0 700
C1 C2 -400 0
D1 D2 0 0
use below query
;With ctef as (select ProductCode , ProductName , ProductStockClosingBalance
row_number()over(partition by ProductCode , ProductName ordered by ProductStockId asc) as r from ProductStock)
, ctef1 as ( select * from ctef where r =1)
, ctel as (select ProductCode , ProductName , ProductStockClosingBalance
row_number()over(partition by ProductCode , ProductName ordered by ProductStockId desc)as r2 from ProductStock )
, ctel1 as ( select * from ctel where r =1)
select f.ProductCode , f.ProductName ,f.ProductStockClosingBalance as typea ,nullif(l.ProductStockClosingBalance ,f.ProductStockClosingBalance ) as typeB
from ctef1 f left join ctel1 s
on f.ProductCode =s.ProductCode and f.ProductName =s.ProductName
if using sql servere 2012 or above Analytic Functions FIRST_VALUE (Transact-SQL) and LAST_VALUE (Transact-SQL) can be used .
Related
So I have a table with the following columns:
Type Test Min Max
-----------------------------
1 a 1 2
1 b Null Null
2 a 0 Null
2 b Null 1
Trying to get all of them like this
Type Test1 Test1 min Test1max Test2 Test2min Test2max
------------------------------------------------------------
1 a 1 2 b Null Null
2 a 0 Null b Null 1
Tried using unpivot first before I use pivot but it's still giving duplicate tests and removing null any help with this is much appreciated
Need null values to show up as well
Select type, result
(
From
Select type, min
From table t
)
Unpivot
(result for minmax in (min1)
)
Select
Thanks
using row_number() and conditional aggregation:
select
[Type]
, Test_1 = max(case when rn = 1 then Test end)
, Test_1_Min = max(case when rn = 1 then Min end)
, Test_1_Max = max(case when rn = 1 then Max end)
, Test_2 = max(case when rn = 2 then Test end)
, Test_2_Min = max(case when rn = 2 then Min end)
, Test_2_Max = max(case when rn = 2 then Max end)
from (
select *
, rn = row_number() over (partition by [Type] order by [Test])
from t
) as s
group by s.Type
rextester demo: http://rextester.com/BKL48976
returns:
+------+--------+------------+------------+--------+------------+------------+
| Type | Test_1 | Test_1_Min | Test_1_Max | Test_2 | Test_2_Min | Test_2_Max |
+------+--------+------------+------------+--------+------------+------------+
| 1 | a | 1 | 2 | b | NULL | NULL |
| 2 | a | 0 | NULL | b | NULL | 1 |
+------+--------+------------+------------+--------+------------+------------+
select *
from table t1
join table t2
on t1.type = t2.type
and t1.test < t2.test
I have a source table that looks like this:
+--------------+----------+------+------------+-----------+
| vehicleindex | parentid | year | make | model |
+--------------+----------+------+------------+-----------+
| 1 | 1 | 2007 | TOYOTA | SIENNA LE |
| 2 | 1 | 2005 | VOLKSWAGEN | JETTA GLS |
+--------------+----------+------+------------+-----------+
I'd like to select from this table such that the output looks like this:
+-------+--------+-----------+-------+------------+-----------+
| year1 | make1 | model1 | year2 | make2 | model2 |
+-------+--------+-----------+-------+------------+-----------+
| 2007 | TOYOTA | SIELLA LE | 2005 | VOLKSWAGEN | JETTA GLS |
+-------+--------+-----------+-------+------------+-----------+
How can I accomplish this on a SQL Server database with a pivot? There will always be either 1 or 2 vehicles in the source table. In the case where there's 1 vehicle, I would expect Year2, Make2 and Model2 to be NULL.
Similar to SQLZim's answer. Only difference is that the Window function Row_Number() is used just in case vehicleindex is not a consistent 1 and 2.
Select year1 = max(case when RN=1 then [year] end)
,make1 = max(case when RN=1 then make end)
,model1 = max(case when RN=1 then model end)
,year2 = max(case when RN=2 then [year] end)
,make2 = max(case when RN=2 then make end)
,model2 = max(case when RN=2 then model end)
From (
Select *
,RN = Row_Number() over (Partition By parentid Order By vehicleindex)
From YourTable
) A
Group By parentid
EDIT: Option 2 - Use PIVOT
Select *
From (
Select parentid
,item = concat(B.item,Dense_Rank() over (Partition By parentid Order By vehicleindex))
,value
From YourTable
Cross Apply ( values ('year' ,cast(Year as varchar(100)))
,('make' ,make)
,('model',model)
) B (item,value)
) A
Pivot (max(value) For [item] in ([year1],[make1],[model1],[year2],[make2],[model2]) ) p
Using conditional aggregation:
select
parentid
, year1 = max(case when vehicleindex=1 then [year] end)
, make1 = max(case when vehicleindex=1 then make end)
, model1 = max(case when vehicleindex=1 then model end)
, year2 = max(case when vehicleindex=2 then [year] end)
, make2 = max(case when vehicleindex=2 then make end)
, model2 = max(case when vehicleindex=2 then model end)
from t
group by parentid
returns:
+----------+-------+------------+-----------+-------+------------+-----------+
| parentid | year1 | make1 | model1 | year2 | make2 | model2 |
+----------+-------+------------+-----------+-------+------------+-----------+
| 1 | 2007 | TOYOTA | SIENNA LE | 2005 | VOLKSWAGEN | JETTA GLS |
| 2 | 2018 | TESLA | MODEL 3 | NULL | NULL | NULL |
+----------+-------+------------+-----------+-------+------------+-----------+
rextestder demo: http://rextester.com/ZTGXU25389
using this test data:
create table t (
vehicleindex int
, parentid int
, [year] int
, make varchar(32)
, model varchar(32)
);
insert into t values
(1,1,2007,'TOYOTA ','SIENNA LE')
, (2,1,2005,'VOLKSWAGEN','JETTA GLS')
, (1,2,2018,'TESLA','MODEL 3')
Unfortunately if you need to pivot 3 columns you need to create 3 separate pivots, then merge them in the end:
SELECT MAX(Year1) as 'Year1', MAX(Make1) as Make1, MAX(Model1) as 'Model1',
MAX(Year2) as 'Year2', MAX(Make2) as Make2, MAX(Model2) as 'Model2'
FROM
(
SELECT parentid,
[1] as 'Year1', NULL as Make1, NULL as 'model1',
[2] as 'Year2', NULL as Make2, NULL as 'model2'
FROM (
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
max([year])
FOR vehicleindex IN ([1],[2])
) AS yearPvt
UNION
SELECT parentid,
NULL as 'Year1', [1] as Make1, NULL as 'model1',
NULL as 'Year2', [2] as Make2, NULL as 'model2'
FROM
(
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
MAX([make])
FOR vehicleindex IN ([1],[2])
)AS makePvt
UNION
SELECT parentid,
NULL as 'Year1', NULL as Make1, [1] as 'model1',
NULL as 'Year2', NULL as Make2, [2] as 'model2'
FROM
(
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
max([model])
FOR vehicleindex IN ([1],[2])
) AS modelPvt
) allPivots
Group BY parentid
I want to rank consecutive null value for my records. Every record will be rank as 1. For the null value that only appear once, the rank will also be 1. But for the null values that appear in a consecutive way, the rank will be 1 for the first record and 2 for the second record and so on. Here's my code.
CREATE TABLE #my_table
(
id BIGINT IDENTITY PRIMARY KEY
,fruit varchar(100)
);
INSERT INTO #my_table
SELECT 'apple'
UNION ALL SELECT 'apple'
UNION ALL SELECT NULL
UNION ALL SELECT 'pineapple'
UNION ALL SELECT 'banana'
UNION ALL SELECT NULL
UNION ALL SELECT NULL
UNION ALL SELECT 'orange'
select * from #my_table
Intended result
+----+-----------+------+
| id | fruit | rank |
+----+-----------+------+
| 1 | apple | 1 |
| 2 | apple | 1 |
| 3 | NULL | 1 |
| 4 | pineapple | 1 |
| 5 | banana | 1 |
| 6 | NULL | 1 |
| 7 | NULL | 2 |
| 8 | orange | 1 |
+----+-----------+------+
How should I query it?
Please help!
You can use difference of ROW_NUMBER to get the grouping of consecutive NULL values:
WITH Cte AS(
SELECT *,
g = ROW_NUMBER() OVER(ORDER BY id)
- ROW_NUMBER() OVER(PARTITION BY fruit ORDER BY id)
FROM #my_table
)
SELECT
id,
fruit,
CASE
WHEN fruit IS NULL THEN ROW_NUMBER() OVER(PARTITION BY fruit, g ORDER BY id)
ELSE 1
END AS rank
FROM Cte
ORDER BY id;
ONLINE DEMO
CREATE TABLE #my_table
(
id BIGINT IDENTITY PRIMARY KEY
,fruit varchar(100)
);
INSERT INTO #my_table
SELECT 'apple'
UNION ALL SELECT 'apple'
UNION ALL SELECT NULL
UNION ALL SELECT 'pineapple'
UNION ALL SELECT 'banana'
UNION ALL SELECT NULL
UNION ALL SELECT NULL
UNION ALL SELECT 'orange'
;
WITH REC_CTE (id,fruit,ranks)
AS (
-- Anchor definition
SELECT id,
fruit,
1 as ranks
FROM #my_table
WHERE fruit is not null
-- Recursive definition
UNION ALL
SELECT son.id,
son.fruit,
case when son.fruit is null AND father.fruit is null then
father.ranks + 1
else
1
end as ranks
FROM #my_table son INNER JOIN
REC_CTE father
on son.id = father.id +1
WHERE son.fruit is null
--AND father.fruit is null
)
SELECT * from REC_CTE order by id
DROP TABLE #my_table
Following solution doesn't use recursion (limited to 32767 level = ~ rows depending on solution) and also it uses only two agregate/ranking functions (SUM and DENSE_RANK):
;WITH Base
AS (
SELECT *, IIF(fruit IS NULL, SUM(IIF(fruit IS NOT NULL, 1, 0)) OVER(ORDER BY id), NULL) AS group_num
FROM #my_table t
)
SELECT *, IIF(fruit IS NULL, DENSE_RANK() OVER(PARTITION BY group_num ORDER BY id), 1) rnk
FROM Base b
ORDER BY id
Results:
id fruit group_num rnk
--- --------- --------- ---
100 apple NULL 1
125 apple NULL 1
150 NULL 2 1
175 pineapple NULL 1
200 banana NULL 1
225 NULL 4 1
250 NULL 4 2
275 orange NULL 1
300 NULL 5 1
325 NULL 5 2
350 NULL 5 3
Here's the data:
[ TABLE_1 ]
id | prod1 | date1 | prod2 | date2 | prod3 | date3 |
---|--------|--------|--------|--------|--------|-------|
1 | null | null | null | null | null | null |
2 | null | null | null | null | null | null |
3 | null | null | null | null | null | null |
[ TABLE_2 ]
id | date | product |
-----|-------------|-----------|
1 | 20140101 | X |
1 | 20140102 | Y |
1 | 20140103 | Z |
2 | 20141201 | data |
2 | 20141201 | Y |
2 | 20141201 | Z |
3 | 20150101 | data2 |
3 | 20150101 | data3 |
3 | 20160101 | X |
Both tables have other columns not listed here.
date is formatted: yyyymmdd and datatype is int.
[ TABLE_2 ] doesn't have empty rows, just tried to make sample above more readable.
Here's the Goal:
I need to update [ TABLE_1 ] prod1,date1,prod2,date2,prod3,date3
with product collected from [ TABLE_2 ] with corresponding date values.
Data must be sorted so that "latest" product becomes prod1,
2nd latest product will be prod2 and 3rd is prod3.
Latest product = biggest date (int).
If dates are equal, order doesn't matter. (see id=2 and id=3).
Updated [ TABLE_1 ] should be:
id | prod1 | date1 | prod2 | date2 | prod3 | date3 |
---|--------|----------|--------|----------|--------|----------|
1 | Z | 20140103 | Y | 20140102 | X | 20140101 |
2 | data | 20141201 | Y | 20141201 | Z | 20141201 |
3 | X | 20160101 | data2 | 20150101 | data3 | 20150101 |
Ultimate goal is to get the following :
[ TABLE_3 ]
id | order1 | order2 | order3 | + Columns from [ TABLE_1 ]
---|--------------------|----------------------|------------|--------------------------
1 | 20140103:Z | 20140102:Y | 20140103:Z |
2 | 20141201:data:Y:Z | NULL | NULL |
3 | 20160101:X | 20150101:data2:data3 | NULL |
I have to admit this exceeds my knowledge and I haven't tried anything.
Should I do it with JOIN or SELECT subquery?
Should I try to make it in one SQL -clause or perhaps in 3 steps,
each prod&date -pair at the time ?
What about creating [ TABLE_3 ] ?
It has to have columns from [ TABLE_1 ].
Is it easiest to create it from [ TABLE_2 ] -data or Updated [ TABLE_1 ] ?
Any help would be highly appreciated.
Thanks in advance.
I'll post some of my own shots on comments.
After looking into it (after my comment), a stored procedure would be best, that you can call to view the data as a pivot, and do away with TABLE_1. Obviously if you need to make this dynamic, you'll need to look into dynamic pivots, it's a bit of a hack with CTEs:
CREATE PROCEDURE DBO.VIEW_AS_PIVOTED_DATA
AS
;WITH CTE AS (
SELECT ID, [DATE], 'DATE' + CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [DATE] DESC) AS VARCHAR) AS [RN]
FROM TABLE_2)
, CTE2 AS (
SELECT ID, PRODUCT, 'PROD' + CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [DATE] DESC) AS VARCHAR) AS [RN]
FROM TABLE_2)
, CTE3 AS (
SELECT ID, [DATE1], [DATE2], [DATE3]
FROM CTE
PIVOT(MAX([DATE]) FOR RN IN ([DATE1],[DATE2],[DATE3])) PIV)
, CTE4 AS (
SELECT ID, [PROD1], [PROD2], [PROD3]
FROM CTE2
PIVOT(MAX(PRODUCT) FOR RN IN ([PROD1],[PROD2],[PROD3])) PIV)
SELECT A.ID, [PROD1], [DATE1], [PROD2], [DATE2], [PROD3], [DATE3]
FROM CTE3 AS A
JOIN CTE4 AS B
ON A.ID=B.ID
Construction:
WITH ranked AS (
SELECT [id]
,[date]
,[product]
,row_number() over (partition by id order by date desc) rn
FROM [sistemy].[dbo].[TABLE_2]
)
SELECT id, [prod1],[date1],[prod2],[date2],[prod3],[date3]
FROM
(
SELECT id, type+cast(rn as varchar(1)) col, value
FROM ranked
CROSS APPLY
(
SELECT 'date', CAST([date] AS varchar(8))
UNION ALL
SELECT 'prod', product
) ca(type, value)
) unpivoted
PIVOT
(
max(value)
for col IN ([prod1],[date1],[prod2],[date2],[prod3],[date3])
) pivoted
You need to take a few steps to achive the aim.
Rank your products by date:
SELECT [id]
,[date]
,[product]
,row_number() over (partition by id order by date desc) rn
FROM [sistemy].[dbo].[TABLE_2]
Unpivot your date and product columns into one column. You can use UNPIVOT OR CROSS APPLY statements. I prefer CROSS APPLY
SELECT id, type+cast(rn as varchar(1)) col, value
FROM ranked
CROSS APPLY
(
SELECT 'date', CAST([date] AS varchar(8))
UNION ALL
SELECT 'prod', product
) ca(type, value)
or the same result using UNPIVOT
SELECT id, type+cast(rn as varchar(1)) col, value
FROM (
SELECT [id],
rn,
CAST([date] AS varchar(500)) date,
CAST([product] AS varchar(500)) prod
FROM ranked) t
UNPIVOT
(
value FOR type IN (date, product)
) unpvt
and at last you use PIVOTE and get a result.
I have these tables: Stock, Unit, Location, Category, StockBalance
At StockBalance: there is StockID from Stock, UnitId from Unit, LocationID from Location
I save at StockBalance Table like following
StockBalanceID | StockID | UnitID | LocationID | BalanceQuantity
1 | 1 | 1 | 1 | 20
2 | 1 | 2 | 1 | 30
3 | 1 | 3 | 1 | 40
4 | 2 | 1 | 2 | 20
5 | 2 | 2 | 2 | 30
6 | 2 | 3 | 2 | 40
I would like to show on Classic ASP as :
Group By : CategoryName
Stock Name Quantity Location Name
Qty | Unit | Qty | Unit | Qty | Unit
Stock One | 20 | One | 30 | Two | 40 | Three | Location One
Stock Two | 20 | One | 30 | Two | 40 | Three | Location Two
How can I Select From StockBalance to get like above? How about my edit one?
Please help me !
PIVOT is what you want.
First some sample data setup:
create table yourTable (
StockBalanceID int, StockID int, UnitID int, LocationID int, BalanceQuantity int);
insert yourTable
select 1 , 1 , 1 , 1 , 20
union all select 2 , 1 , 2 , 1 , 30
union all select 3 , 1 , 3 , 1 , 40
union all select 4 , 2 , 1 , 2 , 20
union all select 5 , 2 , 2 , 2 , 30
union all select 6 , 2 , 3 , 2 , 40
;
Now to do the work...
select StockID, LocationID,
sum(case UnitID when 1 then BalanceQuantity end) as [Unit One],
sum(case UnitID when 2 then BalanceQuantity end) as [Unit Two],
sum(case UnitID when 3 then BalanceQuantity end) as [Unit Three]
from yourTable
group by StockID, LocationID;
Try the following
DECLARE #StockBalances TABLE(
StockBalanceID INT,
StockID INT,
UnitID INT,
LocationID INT,
BalanceQuantity FLOAT
)
DECLARE #Stock TABLE(
StockID INT,
StockName VARCHAR(10)
)
DECLARE #Unit TABLE(
UnitID INT,
UnitName VARCHAR(10)
)
DECLARE #Location TABLE(
LocationID INT,
LocationName VARCHAR(10)
)
INSERT INTO #StockBalances SELECT 1,1,1,1,20
INSERT INTO #StockBalances SELECT 2,1,2,1,30
INSERT INTO #StockBalances SELECT 3,1,3,1,40
INSERT INTO #StockBalances SELECT 4,2,1,2,20
INSERT INTO #StockBalances SELECT 5,2,2,2 ,30
INSERT INTO #StockBalances SELECT 6,2,3,2,40
INSERT INTO #Stock SELECT 1, 'Stock 1'
INSERT INTO #Stock SELECT 2, 'Stock 2'
INSERT INTO #Unit SELECT 1, 'Unit 1'
INSERT INTO #Unit SELECT 2, 'Unit 2'
INSERT INTO #Unit SELECT 3, 'Unit 3'
INSERT INTO #Location SELECT 1, 'Location 1'
INSERT INTO #Location SELECT 2, 'Location 2'
SELECT *
FROM (
SELECT s.StockName,
sb.BalanceQuantity,
u.UnitName,
l.LocationName
FROM #StockBalances sb INNER JOIN
#Stock s ON sb.StockID = s.StockID INNER JOIN
#Unit u ON sb.UnitID = u.UnitID INNER JOIN
#Location l ON sb.LocationID = l.LocationID
) t
PIVOT (SUM(BalanceQuantity) FOR UnitName IN ([Unit 1], [Unit 2], [Unit 3])) p