Row_Number simulation in Sql server 2000 - sql

I have a sample input table as
Declare #input TABLE(Name VARCHAR(8))
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Joseph')
INSERT INTO #input(Name) values('Vicky')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Vicky')
INSERT INTO #input(Name) values('Padukon')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Vick')
INSERT INTO #input(Name) values('Padukon')
INSERT INTO #input(Name) values('Joseph')
INSERT INTO #input(Name) values('Marya')
INSERT INTO #input(Name) values('Vicky')
Also I have a tally table as under
declare #t table(n int)
insert into #t select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 union all select 7 union all select 8 union all
select 9 union all select 10 union all select 11 union all
select 12 union all select 13 union all select 14 union all
select 15 union all select 16 union all select 17 union all
select 18 union all select 19 union all select 20
In Sql Server 2005 if I do as
Select rn, name from (
select ROW_NUMBER()over (order by Name) as rn , * from #input) x
where rn % 2 <> 0
I get the output as
rn name
1 Aryan
3 Aryan
5 Jaesmin
7 Jaesmin
9 Joseph
11 Padukon
13 Vick
15 Vicky
Bu I am restricted to Sql server 2000. How can I get the same output?
I have tried with
SELECT name, (SELECT COUNT(*) FROM #input AS i2 WHERE i2.Name <= i1.Name) As rn
FROM #input AS i1
but the output is wrong
name rn
Aryan 4
Aryan 4
Joseph 9
Vicky 16
Jaesmin 7
Aryan 4
Jaesmin 7
Vicky 16
Padukon 12
Aryan 4
Jaesmin 7
Vick 13
Padukon 12
Joseph 9
Marya 10
Vicky 16

Declare your table variable as
Declare #input TABLE(_id int identity(1, 1), Name VARCHAR(8))
And then reqrite your query as
Select _id, name
from #input
where _id % 2 <> 0

Use this query:
SELECT t1.name, t.n
FROM
(
SELECT a.name, a.c, (SELECT COUNT(*) FROM #input AS i2 WHERE i2.Name <= a.Name) [rn]
FROM
(
SELECT i.name, count(*) c
FROM #input i
GROUP BY i.name
)a
)t1
JOIN #t t ON t.n <= t1.rn
WHERE t.n > t1.rn - t1.c
It produces desired output:
name n
-------- -----------
Aryan 1
Aryan 2
Aryan 3
Aryan 4
Jaesmin 5
Jaesmin 6
Jaesmin 7
Joseph 8
Joseph 9
Marya 10
Padukon 11
Padukon 12
Vick 13
Vicky 14
Vicky 15
Vicky 16

Related

How to enumerate a continuous group of rows based on order in result of sql query

I'm trying to get a column like the desired_output column below and have tried functions like DENSE_RANK and ROW_NUMBER, but can't seem to get the right combination here. I don't have a key to identify the "transition" between continuous groups.
So each time it hits a new group per id_key, regardless of if it already hit that group before (ordered by sequence) I'd like to start a new "transition"
Ultimately the goal is to get something like the MIN and MAX sequence per desired_output which I was just going to do with GROUP BY. I was able to accomplish this using a complex query of LEAD/LAG/JOINS, but want to see if anyone knows of a DENSE_RANK-like function (or way to use DENSE_RANK) that would give the desired_output below (to paint the full picture).
Thanks !
id_key sequence group desired_output
1 1 A 1
1 2 A 1
1 3 B 2
1 4 B 2
1 5 B 2
1 6 B 2
1 7 C 3
1 8 C 3
1 9 C 3
1 10 B 4
1 11 B 4
1 12 B 4
1 13 C 5
1 14 C 5
2 15 A 1
2 16 A 1
2 17 B 2
2 18 B 2
2 19 C 3
2 20 C 3
2 21 B 4
2 22 C 5
2 23 C 5
2 24 C 5
Try this:
SELECT id_key, sequence, [group],
SUM(flag) OVER (PARTITION BY id_key
ORDER BY sequence) + 1 AS desired_output
FROM (
SELECT id_key, sequence, [group],
IIF(LAG([group]) OVER (PARTITION BY id_key
ORDER BY sequence) <> [group], 1, 0) AS flag
FROM mytable) AS t
ORDER BY sequence
flag field flags any changes in [group] column within each id_key partition. The outer query uses flag field to calculate a running total, so as to account for all [group] changes in each id_key partition.
SQL Dense_Rank() function is the correct way of solving this requirement I guess.
What you might have missed can be the Partition By clause which can be used in window functions.
Here is a Select query that might help
--create table myrows (id_key int, [sequence] int, [group] varchar(3))
/*
insert into myrows select 1 , 1 ,'A'
insert into myrows select 1 , 2 ,'A'
insert into myrows select 1 , 3 ,'B'
insert into myrows select 1 , 4 ,'B'
insert into myrows select 1 , 5 ,'B'
insert into myrows select 1 , 6 ,'B'
insert into myrows select 1 , 7 ,'C'
insert into myrows select 1 , 8 ,'C'
insert into myrows select 1 , 9 ,'C'
insert into myrows select 1 , 10 ,'B'
insert into myrows select 1 , 11 ,'B'
insert into myrows select 1 , 12 ,'B'
insert into myrows select 1 , 13 ,'C'
insert into myrows select 1 , 14 ,'C'
insert into myrows select 2 , 15 ,'A'
insert into myrows select 2 , 16 ,'A'
insert into myrows select 2 , 17 ,'B'
insert into myrows select 2 , 18 ,'B'
insert into myrows select 2 , 19 ,'C'
insert into myrows select 2 , 20 ,'C'
insert into myrows select 2 , 21 ,'B'
insert into myrows select 2 , 22 ,'C'
insert into myrows select 2 , 23 ,'C'
insert into myrows select 2 , 24 ,'C'
*/
select *,
DENSE_RANK() over (partition by id_key order by [group]) desired_output
from myrows

dynamic PIVOT query with SQL Server

I have the following table Account
Accountid Calcul
1 27+23+12
4 5+9+12
7 7+12+20
I am looking to get the following table AccountTemp
Accountid AccountCode
1 27
1 23
1 12
4 5
4 9
4 12
7 7
7 12
7 20
declare #account table(Accountid int, Calcul varchar(20))
--AccountTemp
insert #account values(1, '27+23+12')
insert #account values(4,'5+9+12')
insert #account values(7,'7+12+20')
create table #accounttemp(Accountid int, AccountCode int)
insert #accounttemp(Accountid, AccountCode)
SELECT Accountid, t.c.value('.', 'INT') AccountCode
FROM (
SELECT Accountid, CAST('<t>' +
REPLACE(Calcul, '+', '</t><t>') + '</t>' AS XML) x
FROM #account
) a
CROSS APPLY x.nodes('/t') t(c)
select * from #accounttemp
drop table #accounttemp
Result:
Accountid AccountCode
1 27
1 23
1 12
4 5
4 9
4 12
7 7
7 12
7 20
You could do this:
Create a split function like this:
CREATE FUNCTION [dbo].[Split]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
GO
Then do a query like this:
SELECT
tbl.Accountid,
split.AccountCode
FROM
#tbl AS tbl
CROSS APPLY
(
SELECT
CAST(calc.Data AS INT) AS AccountCode
FROM
dbo.split(tbl.Calcul,'+') as calc
) as split
Output:
Accountid AccountCode
1 27
1 23
1 12
4 5
4 9
4 12
7 7
7 12
7 20
Reference:
Split a string to a table using T-SQL
CROSS APPLY Explained

Produce all row groups having a SUM between two values

I have table as below(products)
id name quality weight
1 Demir-1 ST-1 10
2 Demir-2 ST-2 7
3 Demir-3 ST-1 20
4 Demir-2 ST-3 8
5 Demir-1 ST-3 6
6 Demir-4 ST-2 10
7 Demir-2 ST-2 12
8 Demir-1 ST-1 15
9 Demir-1 ST-3 10
10 Demir-3 ST-3 5
11 Demir-2 ST-2 5
Now if a user wants to get list of products which sum of weight will between 20 and 25 and name='Result-2' then the result should be as below. All combination must be shown where sum of weight between 20 and 25.
id name quality weight
4 Demir-2 ST-3 8
7 Demir-2 ST-2 12
11 Demir-2 ST-2 5
------------------------------
2 Demir-2 ST-2 7
4 Demir-2 ST-3 8
11 Demir-2 ST-2 5
------------------------------
------------------------------
1 Demir-1 ST-1 10
8 Demir-1 ST-1 15
You can see that the sum(weight) is 25.
DECLARE #tblProducts TABLE(Id INT IDENTITY, ProductName varchar(50),Quality varchar(50), ProductWeight int)
INSERT INTO #tblProducts SELECT 'Demir-1','ST-1',10
INSERT INTO #tblProducts SELECT 'Demir-2','ST-2',7
INSERT INTO #tblProducts SELECT 'Demir-3','ST-1',20
INSERT INTO #tblProducts SELECT 'Demir-2','ST-3',8
INSERT INTO #tblProducts SELECT 'Demir-1','ST-3',6
INSERT INTO #tblProducts SELECT 'Demir-4','ST-2',10
INSERT INTO #tblProducts SELECT 'Demir-2','ST-2' ,12
INSERT INTO #tblProducts SELECT 'Demir-1','ST-1',15
INSERT INTO #tblProducts SELECT 'Demir-1','ST-3',10
INSERT INTO #tblProducts SELECT 'Demir-3','ST-3' ,5
INSERT INTO #tblProducts SELECT 'Demir-2' ,'ST-2 ',5
;WITH Cte (Id,ProductQuality,ProductIds,ProductNames,ProductQualities,ProductTotalWeight,ProductWeights,ProductName1,ProductName2) AS
(
SELECT Id
,Quality
, ',' + CAST(Id AS VARCHAR(MAX))
,',' + CAST(ProductName AS VARCHAR(MAX))
,',' + CAST(Quality AS VARCHAR(MAX))
,ProductWeight
, ',' + CAST(ProductWeight AS VARCHAR(MAX))
,ProductName
,CAST(ProductName AS VARCHAR(MAX))
FROM #tblProducts
UNION ALL
SELECT p.Id
, p.Quality
,c.ProductIds + ',' + CAST(p.Id AS VARCHAR(MAX))
,c.ProductNames + ',' + CAST(p.ProductName AS VARCHAR(MAX))
,c.ProductQualities + ',' + CAST(p.Quality AS VARCHAR(MAX))
,c.ProductTotalWeight + p.ProductWeight
,c.ProductWeights + ',' + CAST(p.ProductWeight AS VARCHAR(MAX))
,p.ProductName
,c.ProductName2
FROM #tblProducts AS p JOIN Cte c ON p.Id < c.Id
WHERE p.ProductName = c.ProductName2
)
SELECT
ProductIds = STUFF(ProductIds,1,1,'')
,ProductNames = STUFF(ProductNames,1,1,'')
,ProductQualities = STUFF(ProductQualities,1,1,'')
,ProductTotalWeight
,ProductWeights = STUFF(ProductWeights,1,1,'')
FROM CTE
WHERE ProductTotalWeight BETWEEN 20 AND 25
Result
ProductIds ProductNames ProductQualities ProductTotalWeight ProductWeights
3 Demir-3 ST-1 20 20
11,7,2 Demir-2,Demir-2,Demir-2 ST-2 ,ST-2,ST-2 24 5,12,7
11,7,4 Demir-2,Demir-2,Demir-2 ST-2 ,ST-2,ST-3 25 5,12,8
11,4,2 Demir-2,Demir-2,Demir-2 ST-2 ,ST-3,ST-2 20 5,8,7
10,3 Demir-3,Demir-3 ST-3,ST-1 25 5,20
9,1 Demir-1,Demir-1 ST-3,ST-1 20 10,10
9,8 Demir-1,Demir-1 ST-3,ST-1 25 10,15
8,1 Demir-1,Demir-1 ST-1,ST-1 25 15,10
8,5 Demir-1,Demir-1 ST-1,ST-3 21 15,6
7,4 Demir-2,Demir-2 ST-2,ST-3 20 12,8
Unfortunately, SQL is not the correct coding engine for coming up with all combinations from a table with a given calculation, as SQL code I hacked together shows.
Normally I would recommend this is coded in a procedural language given it's nature.
Note also that the solution here, with larger input data sets can quickly exhaust SQL's recursion limit. The number of possible products can also grow to sufficently large numbers that it will run very slowly.
drop table products
drop table #MinW
drop table #Products
go
create table products
(
id int,
name varchar(20),
quality varchar(20),
[weight] int
)
go
insert into products
select
1, 'Demir-1', 'ST-1', 10
union
select 2, 'Demir-2', 'ST-2', 7
union
select 3, 'Demir-3', 'ST-1', 20
union
select 4, 'Demir-2', 'ST-3', 8
union
select 5, 'Demir-1', 'ST-3', 6
union
select 6, 'Demir-4', 'ST-2', 10
union
select 7, 'Demir-2', 'ST-2', 12
union
select 8, 'Demir-1', 'ST-1', 15
union
select 9, 'Demir-1', 'ST-3', 10
union
select 10, 'Demir-3', 'ST-3', 5
union
select 11, 'Demir-2', 'ST-2', 5
go
select
p.id, p.name, p.quality, p.weight
into #MinW
from products p
inner join
(
select name,min(weight) as weight
from products group by name having min(weight) < 25
) as minW
on p.name = minW.name and p.weight = minW.weight
go
with weightProducts (id, name, [weight],history)
as
(
select id, name,[weight],convert(varchar(1024),'"'+convert(varchar(10),id)+'",') from #minW
union all
select products.id, products.name,products.[weight] + weightProducts.[weight],convert(varchar(1024),weightProducts.history+'"'+convert(varchar(10),products.id)+'",') from products, weightProducts where products.name = weightproducts.name and charindex('"'+convert(varchar(10),products.id)+'",',weightProducts.history)<=0 and (products.weight + weightProducts.weight) < 25
)
-- Statement using the CTE
select *
into #Products
from weightProducts
where weight between 20 and 25
go
select
p1.history,
p2.id,
p2.quality,
p2.weight,
p1.weight as totalWeight
from #Products p1
inner join
products p2 on p1.name = p2.name and charindex('"'+convert(varchar(10),p2.id)+'",',p1.history)>0
order by
p1.history,
p2.id
SELECT * FROM tablename
WHERE name IN (
SELECT name FROM tablename
GROUP BY name
HAVING SUM(weight) BETWEEN 20 AND 25)
Try this:
SELECT * FROM products
WHERE name IN (
SELECT name FROM products
GROUP BY name
HAVING SUM(weight) BETWEEN 20 AND 25)
How about this query:
select id, name, quality, weight from PRODUCTS
group by id, name, quality, weight
having sum(weight) between 20 and 25

sql server : count records

I have a tableA (ID int, Match varchar, tot int)
ID Match Tot
1 123
2 123
3 12
4 12
5 4
6 12
7 8
Now, I want to calculate Tot which is total number of match exists in the table. for example 123 occured twice, 12 exist thrice and so on. Also note that I want the count only at first match. here is the expected result.:
ID Match Tot
1 123 2
2 123
3 12 3
4 12
5 4 1
6 12
7 8 1
Another case:
ID Match Count Tot
1 123 2
2 123 1
3 12 10
4 12 10
5 4 3
6 12 5
7 8 7
Now I want to add the count for the same match. expected result:
ID Match Count Tot
1 123 2 3
2 123 1
3 12 10 25
4 12 10
5 4 3 3
6 12 5
7 8 7 7
Thanks
WITH tableA(ID, Match) AS
(
SELECT 1,123 UNION ALL
SELECT 2,123 UNION ALL
SELECT 3,12 UNION ALL
SELECT 4,12 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,12 UNION ALL
SELECT 7,8
)
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Match ORDER BY ID) = 1
THEN COUNT(*) OVER (PARTITION BY Match)
END AS Tot
FROM tableA
ORDER BY ID
SELECT match, COUNT(match ) as Tot
FROM tableA
GROUP BY match
Solution 1:
DECLARE #MyTable TABLE
(
ID INT PRIMARY KEY
,Match VARCHAR(10) NOT NULL
,Tot INT NULL
);
INSERT #MyTable(ID, Match)
SELECT 1, 123
UNION ALL
SELECT 2, 123
UNION ALL
SELECT 3, 12
UNION ALL
SELECT 4, 12
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, 12
UNION ALL
SELECT 7, 8;
--SELECT
SELECT *
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a;
--UPDATE
WITH MyCTE
AS
(
SELECT a.Tot
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a
)
UPDATE MyCTE
SET Tot = TotCalculated;
SELECT *
FROM #MyTable;
Solution 2:
UPDATE #MyTable
SET Tot = NULL;
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
ORDER BY x.ID
UPDATE #MyTable
SET Tot = t.Num
FROM #MyTable z
INNER JOIN
(
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
) t ON z.ID = t.ID;
SELECT *
FROM #MyTable;

MS Access "Group by" continuous values

I Have the following Access table (Primary Key = Date+Id):
Date Id Value
01/07/2011 00:10:00 5 200
01/07/2011 00:30:00 5 210
01/07/2011 00:40:00 2 458
01/07/2011 00:50:00 2 500
01/07/2011 01:00:00 4 600
01/07/2011 01:10:00 5 359
01/07/2011 01:20:00 5 360
01/07/2011 01:30:00 5 370
01/07/2011 01:40:00 5 380
Of course, the query "SELECT Id, MAX(Value) FROM DATAS GROUP BY Id;" returns:
Id Max
2 500
4 600
5 380
But is it possible in MS Access to have a query which groups by "sequences" of Id?
Expected result:
Id Max
5 210
2 500
4 600
5 380
There is no easy way to solve your problem. Here is a workaround.
declare #t table(date datetime, id int, value int)
insert #t values('01/07/2011 00:10:00',5,200)
insert #t values('01/07/2011 00:30:00',5,210)
insert #t values('01/07/2011 00:40:00',2,458)
insert #t values('01/07/2011 00:50:00',2,500)
insert #t values('01/07/2011 01:00:00',4,600)
insert #t values('01/07/2011 01:10:00',5,359)
insert #t values('01/07/2011 01:20:00',5,360)
insert #t values('01/07/2011 01:30:00',5,370)
insert #t values('01/07/2011 01:40:00',5,380)
;with a as
(
select date, id, value, (select count(*) from #t where t.date > date) rn
from #t t
), b as
(
select a.date, a.id, a.value, coalesce(cast(b.id - a.id as bit),1) d
from a left join a b on a.rn -1 = b.rn
), c as
(
select date,id, value, (select sum(d) from b b2 where date <=b.date) e from b
)
select id, max(value) max
from c group by id, e
Result:
id max
----- ---
5 210
2 500
4 600
5 380