Referencing a value from the previous row - sql

Can anyone help me to write query in Sql Server 2014 for the following condition:
I need to add the first row CurrentKM value in second row LastKM column like that it has to go.
VNo CurrKM LastKM
1 15000 0
2 16000 15000
3 17000 16000

Try using LAG:
Select VNo,CurrKM, LAG(CurrKM,1,0) over (order by VNo asc) LastKM
From Table

declare #t table (id int,currkm int)
insert into #t (id,currkm)values (1,15000),(2,16000),(3,17000)
;with cte as(
select distinct id,currkm,ROW_NUMBER()OVER( order by id desc,currkm desc)R from #t)
select c.id,c.currkm,cc.currkm from cte c
LEFT JOIN cte cc
on cc.R - 1 = c.R
ORDER BY c.id

Related

Finding Occurrence of the duplicate values

I have table with 3 columns (id, Name, Occurrence), I want to update the Occurrence column ,based on the id column, attached snap for the reference.
for example if my id column has "606" value 3 times then my occurrent column should have 3 against all the "606" value.
Below is the method which I tried.
I tried to find the duplicate values using group by and Having clause and saved it in a temp table and from there I tried to join the table value from the temp table.
you can use window functions in an updatable CTE for this.
You haven't supplied any actual sample data so this is untested, however the following should work:
with x as (
select Id, Occurence, count(*) over(partition by Id) qty
from Table
)
update x
set Occurence = Qty;
You can go for GROUP BY based approach also.
declare #TABLE TABLE(ID INT, NAME CHAR(3), occurance int null)
insert into #TABLE VALUES
(1,'AAA',NULL),(1,'AAA',NULL),(2,'CCC',NULL),(3,'DDD',NULL), (3,'DDD',NULL),(4,'EEE',NULL),(5,'FFF',NULL);
;WITH CTE_Table as
(
SELECT ID, COUNT(*) AS Occurance
FROM #table
group by id
)
UPDATE t
SET occurance = c.occurance
FROM #table t
INNER JOIN CTE_Table as c
on C.ID = T.ID
SELECT * FROM #TABLE
ID
NAME
occurance
1
AAA
2
1
AAA
2
2
CCC
1
3
DDD
2
3
DDD
2
4
EEE
1
5
FFF
1
You can use a CTE and calculate row number and update your table base on CTE
;WITH q
AS
(
SELECT Id,COUNT(1) 'RowNum'
FROM YourTable
GROUP BY Id
)
UPDATE YourTable
SET Occurrence=q.RowNum
FROM YourTable t
INNER JOIN q
ON t.Id=q.Id

Repeating rows based on count in a different column - SQL

I have a table that holds IDs and count. I want to repeat the rows the number of times mentioned in the count.
My table:
Desired output:
My code:
create table #temp1(CID int, CVID int, count int)
insert #temp1
values
(9906, 4687, 4),
(9906, 4693, 5)
create table #temp2 (CID int,CVID int, count int,ro int)
;with t3 as (
select c.CID,c.CVID, c.count, row_number() over (partition by c.CID order by c.CID) ro
from #temp1 c
)
insert #temp2
select CID,CVID,count,ro from t3 where ro <= count
My code is missing something that its not producing desired result. Any help?!
You need a numbers table up to the maximum value of count column which can then be used to generate multiple rows. This number generation can be done using a recursive cte.
--Recursive CTE
with nums(n) as (select max(count) from #temp1
union all
select n-1
from nums
where n > 1
)
--Query to generate multiple rows
select t.*,nums.n as ro
from #temp1 t
join nums on nums.n <= t.count
Just another option is an ad-hoc tally table
Example
Select A.*
,Ro = B.N
From YourTable A
Join ( Select Top 1000 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1 ) B on B.N<=A.[Count]
Returns
CID CVID COUNT Ro
9906 4687 4 1
9906 4687 4 2
9906 4687 4 3
9906 4687 4 4
9906 4693 5 1
9906 4693 5 2
9906 4693 5 3
9906 4693 5 4
9906 4693 5 5
I would use a recursive CTE, but directly:
with cte as (
select CID, CVID, count, 1 as ro
from #temp1
union all
select CID, CVID, count, ro + 1
from cte
where cte.ro < cte.count
)
select cte.*
from cte;
If your counts exceed 100, then you'll need to use option (maxrecursion 0).
Thanks all for all the suggestion. I used the below query to solve my problem:
;with cte(cid, cvid,count, i) as
(
select cid
, cvid
, count
, 1
from #temp1
union all
select cid
, cvid
, count
, i + 1
from cte
where cte.i < cte.count
)
select *
from cte
order by
cid,count

SUM Column in SQL

I have a table in SQL Server, and I need to sum a column, like the example below:
CREATE TABLE B
(
ID int,
Qty int,
)
INSERT INTO B VALUES (1,2)
INSERT INTO B VALUES (2,7)
INSERT INTO B VALUES (3,2)
INSERT INTO B VALUES (4,11)
SELECT *, '' AS TotalQty FROM B
ORDER BY ID
In this example what I need is the column TotalQty give me the values like:
2
9
11
22
How can it be achieved?
You can use SUM in a co-related subquery or CROSS APPLY like this
Co-related Subquery
SELECT ID,(SELECT SUM(Qty) FROM B WHERE B.id <= C.id) FROM B as C
ORDER BY ID
Using CROSS APPLY
SELECT ID,D.Qty FROM B as C
CROSS APPLY
(
SELECT SUM(Qty) Qty
FROM B WHERE B.id <= C.id
)AS D
ORDER BY ID
Output
1 2
2 9
3 11
4 22
If you were using SQL Server 2012 or above, SUM() with Over() clause could have been used like this.
SELECT ID, SUM(Qty) OVER(ORDER BY ID ASC) FROM B as C
ORDER BY ID
Edit
Another way to do this in SQL Server 2008 is using Recursive CTE. Something like this.
Note: This method is based on the answer by Roman Pekar on this thread Calculate a Running Total in SQL Server. Based on his observation this would perform better than co related subquery and CROSS APPLY both
;WITH CTE as
(
SELECT ID,Qty,ROW_NUMBER()OVER(ORDER BY ID ASC) as rn
FROM B
), CTE_Running_Total as
(
SELECT Id,rn,Qty,Qty as Running_Total
FROM CTE
WHERE rn = 1
UNION ALL
SELECT C1.Id,C1.rn,C1.Qty,C1.Qty + C2.Running_Total as Running_Total
FROM CTE C1
INNER JOIN CTE_Running_Total C2
ON C1.rn = C2.rn + 1
)
SELECT *
FROM CTE_Running_Total
ORDER BY Id
OPTION (maxrecursion 0)

Split a row on 2 or more rows depending on a column

I have a question
If I have one row that looks like this
|ordernumber|qty|articlenumber|
| 123125213| 3 |fffff111 |
How can I split this into three rows like this:
|ordernumber|qty|articlenumber|
| 123125213| 1 |fffff111 |
| 123125213| 1 |fffff111 |
| 123125213| 1 |fffff111 |
/J
You can use recursive CTE:
WITH RCTE AS
(
SELECT
ordernumber, qty, articlenumber, qty AS L
FROM Table1
UNION ALL
SELECT
ordernumber, 1, articlenumber, L - 1 AS L
FROM RCTE
WHERE L>0
)
SELECT ordernumber,qty, articlenumber
FROM RCTE WHERE qty = 1
SQLFiddleDEMO
EDIT:
Based on Marek Grzenkowicz's answer and MatBailie's comment, whole new idea:
WITH CTE_Nums AS
(
SELECT MAX(qty) n FROM dbo.Table1
UNION ALL
SELECT n-1 FROM CTE_Nums
WHERE n>1
)
SELECT ordernumber ,
1 AS qty,
articlenumber
FROM dbo.Table1 t1
INNER JOIN CTE_Nums n ON t1.qty >= n.n
Generating number from 1 to max(qty) and join table on it.
SQLFiddle DEMO
Here's a quick hack using an additional table populated with a number of rows suitable for the qty values you are expecting:
-- helper table
CREATE TABLE qty_splitter (qty int)
INSERT INTO qty_splitter VALUES (1)
INSERT INTO qty_splitter VALUES (2)
INSERT INTO qty_splitter VALUES (3)
INSERT INTO qty_splitter VALUES (4)
INSERT INTO qty_splitter VALUES (5)
....
-- query to produce split rows
SELECT t1.ordernumber, 1, t1.articlenumber
FROM table1 t1
INNER JOIN qty_splitter qs on t.qty >= qs.qty
You can do it using CTE
declare #t table (ordername varchar(50), qty int)
insert into #t values ('ord1',5),('ord2',3)
;with cte as
(
select ordername, qty, qty-1 n
from #t
union all
select ordername, qty, n-1
from cte
where n>0
)
select ordername,1
from cte
order by ordername
Also you can use option with master..spt_values system table.
SELECT t.ordernumber, o.qty, t.articlenumber
FROM dbo.SplitTable t CROSS APPLY (
SELECT 1 AS qty
FROM master..spt_values v
WHERE v.TYPE = 'P' AND v.number < t.qty
) o
However, for this purpose is preferable to use its own sequence table
See demo on SQLFiddle

How can I expand out a row into multiple row result set?

I have a table that I'm trying to break out each row into one or more rows based on the second column value. Like this:
table (id, pcs):
ABC 3
DEF 1
GHJ 4
query result (id, pcs_num):
ABC 1
ABC 2
ABC 3
DEF 1
GHJ 1
GHJ 2
GHJ 3
GHJ 4
I'm writing this as a sproc in SQL server 2008. My best solution is to use a cursor and add [pcs] number of rows to a temp table for each row in the table. Is seems like there must be a simpler solution than this that I am missing. Thanks.
You can use a recursive CTE:
;WITH CTE AS
(
SELECT *
FROM YourTable
UNION ALL
SELECT id, pcs-1
FROM CTE
WHERE pcs-1 >= 1
)
SELECT *
FROM CTE
ORDER BY id, pcs
OPTION(MAXRECURSION 0)
Here is a demo for you to try.
Here is my approach. Extremely easy with a Tally Table (A table that only has a column with a value 1 -> X). No need for recursion, and this will be much faster over larger tables.
Notice we are only making a Tally Table of 100 rows, feel free to expand that as large as you'd like. If you get too crazy, you might need another cross join in sys.sysobjects to accomdate. The real query is at the bottom, as you can see it's extremely easy.
SELECT TOP 100
IDENTITY( INT,1,1 ) AS N
INTO #Tally
FROM sys.sysobjects sc1 ,
sys.sysobjects sc2
CREATE TABLE #Test
(
Id char(3),
pcs int
)
INSERT INTO #Test
SELECT 'ABC', 3 UNION ALL
SELECT 'DEF', 1 UNION ALL
SELECT 'GHJ', 4
SELECT #Test.Id, #Tally.N FROM #Tally
JOIN #Test ON #Tally.N <= #Test.pcs
ORDER BY #Test.Id
SELECT
id
,pcs_num
FROM MyTable
CROSS APPLY (
SELECT TOP (pcs)
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) pcs_num
FROM master.dbo.spt_values
) t