SQL concat integers and group them with from to - sql-server

I'm new to stackoverflow, but I'm stuck with my query.
I've got a SQL table whitch looks like this:
+-------+------------+
| col1 | col2 |
+-------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 6 |
+-------+------------+
I don't know how to get the following resultset:
+-------+------------+
| col1 |SerialNumber|
+-------|------------+
| 1 | 1 to 4, 6 |
+--------------------+
With XML Path i can get this:
+-------+------------+
| col1 |SerialNumber|
+-------|------------+
| 1 | 1,2,3,4,6, |
+--------------------+
This is my query for it:
SELECT DISTINCT O.Col1,
(SELECT CAST(P.Col2 As varchar(5)) + ',' AS [text()]
FROM #Test P
WHERE P.Col1 = O.Col1
ORDER BY P.Col1
FOR XML PATH('')) AS 'SerialNumber'
FROM #Test O
I'm sorry if my question was already asked. I'm also lacking Keywords for this topic.

Test data:
CREATE TABLE t(col1 int,col2 int)
INSERT t(col1,col2)VALUES
(1,1),(1,2),(1,3),(1,4),
(1,6),(1,7),(1,8),(1,9),
(1,11),
(1,13),
(2,3),(2,4),(2,5),
(2,7)
A variant with FOR XML PATH:
SELECT col1,col2,outVal
INTO #temp
FROM
(
SELECT
col1,
col2,
outVal,
ISNULL(LEAD(outVal)OVER(PARTITION BY col1 ORDER BY col2),'') nextOutVal
FROM
(
SELECT
col1,
col2,
CASE
WHEN col2-1=LAG(col2)OVER(PARTITION BY col1 ORDER BY col2) AND col2+1=LEAD(col2)OVER(PARTITION BY col1 ORDER BY col2)
THEN 'to'
ELSE CAST(col2 AS varchar(10))
END outVal
FROM t
) q
) q
WHERE outVal<>nextOutVal
ORDER BY col1,col2
SELECT
t1.col1,
REPLACE(STUFF(
(
SELECT ','+t2.outVal
FROM #temp t2
WHERE t2.col1=t1.col1
ORDER BY t2.col2
FOR XML PATH('')
),1,1,''),',to,',' to ') SerialNumber
FROM (SELECT DISTINCT col1 FROM #temp) t1
DROP TABLE #temp
A variant for SQL Server 2017 (with STRING_AGG):
SELECT
col1,
REPLACE(STRING_AGG(outVal,',')WITHIN GROUP(ORDER BY col2),',to,',' to ')
FROM
(
SELECT
col1,
col2,
outVal,
ISNULL(LEAD(outVal)OVER(PARTITION BY col1 ORDER BY col2),'') nextOutVal
FROM
(
SELECT
col1,
col2,
CASE
WHEN col2-1=LAG(col2)OVER(PARTITION BY col1 ORDER BY col2) AND col2+1=LEAD(col2)OVER(PARTITION BY col1 ORDER BY col2)
THEN 'to'
ELSE CAST(col2 AS varchar(10))
END outVal
FROM t
) q
) q
WHERE outVal<>nextOutVal
GROUP BY col1
Result:
col1 SerialNumber
1 1 to 4,6 to 9,11,13
2 3 to 5,7

Solution:
Another possible approach using CTE for start and end values for each sequence and group concatenation:
T-SQL:
-- Table creation
CREATE TABLE #ValuesTable (
Col1 int,
Col2 int
)
INSERT INTO #ValuesTable VALUES (1, 1)
INSERT INTO #ValuesTable VALUES (1, 2)
INSERT INTO #ValuesTable VALUES (1, 3)
INSERT INTO #ValuesTable VALUES (1, 4)
INSERT INTO #ValuesTable VALUES (1, 6)
INSERT INTO #ValuesTable VALUES (2, 1)
INSERT INTO #ValuesTable VALUES (2, 2)
INSERT INTO #ValuesTable VALUES (2, 3)
INSERT INTO #ValuesTable VALUES (2, 4)
INSERT INTO #ValuesTable VALUES (2, 6)
INSERT INTO #ValuesTable VALUES (2, 7);
INSERT INTO #ValuesTable VALUES (2, 10);
-- Find sequences
WITH
TableStart AS (
SELECT t.Col1, t.Col2, ROW_NUMBER() OVER (ORDER BY t.Col1, t.Col2) AS RN
FROM #ValuesTable t
LEFT JOIN #ValuesTable b ON (t.Col1 = b.Col1) AND (t.Col2 = b.Col2 + 1)
WHERE (b.Col2 IS NULL)
),
TableEnd AS (
SELECT t.Col1, t.Col2, ROW_NUMBER() OVER (ORDER BY t.Col1, t.Col2) AS RN
FROM #ValuesTable t
LEFT JOIN #ValuesTable b ON (t.Col1 = b.Col1) AND (t.Col2 = b.Col2 - 1)
WHERE (b.Col2 IS NULL)
),
TableSequences AS (
SELECT
TableStart.Col1 AS Col1,
CASE
WHEN (TableStart.Col2 <> TableEnd.Col2) THEN CONVERT(nvarchar(max), TableStart.Col2) + N' to ' + CONVERT(nvarchar(max), TableEnd.Col2)
ELSE CONVERT(nvarchar(max), TableStart.Col2)
END AS Sequence
FROM TableStart
LEFT JOIN TableEnd ON (TableStart.RN = TableEnd.RN)
)
-- Select with group concatenation
SELECT
t1.Col1,
(
SELECT t2.Sequence + N', '
FROM TableSequences t2
WHERE t2.Col1 = t1.Col1
ORDER BY t2.Col1
FOR XML PATH('')
) SerialNumber
FROM (SELECT DISTINCT Col1 FROM TableSequences) t1
Output:
Col1 SerialNumber
1 1 to 4, 6,
2 1 to 4, 6 to 7, 10,
Notes:
Tested on SQL Server 2005, 2012, 2016.

Related

Tree data to flat data in SQL Server

I am facing the problem tree view to flat view, I have some data that has come from a tree, those are stored in a table that is presenting in my picture below left. I am saying the top level of tree is level1, and second level is level2 and so on. My expected result that is representing in my picture below right.
How can I convert my data to my expected result dynamically in SQL Server? We have to create dynamic column level1, level2, level3 and so on. Do you have any idea? Thanks.
Here is the sample data
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Id INT NOT NULL,
SomeName VARCHAR(3) NOT NULL,
ParentId INT NULL
);
INSERT #TestData (Id, SomeName, ParentId) VALUES
(1, 'O', NULL),
(2, 'D1', 1),
(3, 'D2', 1),
(4, 'S1', 2),
(5, 'S2', 2),
(6, 'S1', 3),
(7, 'SP1', 3);
SELECT * FROM #TestData td;
You can query as below:
;With Cte as ( --Recursive CTE for traversing tree
Select *, convert(varchar(max),[name]) as NameLevel, 1 as Levl from #TreeData where ParentId is Null
Union all
Select t.Id, t.[Name], t.[ParentId], concat(c.NameLevel,',',t.[name]) as NameLevel, c.Levl + 1 as Levl from Cte c
inner join #TreeData t on c.Id = t.ParentId
)
Select * from (
select c.Id, c.Levl, a.[value]
,RowN = row_number() over(partition by Id order by Levl) from cte c
cross apply string_split(c.NameLevel,',') a
) sq
pivot(max([value]) for RowN in([1],[2],[3])) p --Pivot for getting all data
Output as below:
+---+------+------+----+
| 1 | 2 | 3 | Id |
+---+------+------+----+
| O | NULL | NULL | 1 |
| O | D1 | NULL | 2 |
| O | D2 | NULL | 3 |
| O | D1 | S1 | 4 |
| O | D1 | S2 | 5 |
| O | D2 | S1 | 6 |
| O | D2 | SP1 | 7 |
+---+------+------+----+
Below the input table and data I used:
Create Table #TreeData(Id int, [name] varchar(10), ParentId int)
Insert into #TreeData(id, [name], ParentId) values
(1,'O', null)
,(2,'D1', 1)
,(3,'D2', 1)
,(4,'S1', 2)
,(5,'S2', 2)
,(6,'S1', 3)
,(7,'SP1', 3)
IF OBJECT_ID('tempdb..#TreeData', 'U') IS NOT NULL
DROP TABLE #TreeData;
CREATE TABLE #TreeData (
Id INT NOT NULL,
SomeName VARCHAR(3) NOT NULL,
ParentId INT NULL
);
INSERT #TreeData (Id, SomeName, ParentId) VALUES
(1, 'O', NULL),
(2, 'D1', 1),
(3, 'D2', 1),
(4, 'S1', 2),
(5, 'S2', 2),
(6, 'S1', 3),
(7, 'SP1', 3);
--SELECT * FROM #TestData td;
;With Cte as ( --Recursive CTE for traversing tree
Select Id,SomeName,ParentId, convert(varchar(max),[SomeName]) as NameLevel, 1 as Levl from #TreeData where ParentId is Null
Union all
Select t.Id, t.[SomeName], t.[ParentId], (c.NameLevel +','+ t.[SomeName]) as NameLevel, c.Levl + convert(int, 1) as Levl
from Cte c
inner join #TreeData t on c.Id = t.ParentId
)
--select * from cte
Select * from (
select c.Id, c.Levl, a.Items
,RowN = row_number() over(partition by Id order by Levl) from cte c
cross apply split(c.NameLevel,',') a
) sq
pivot(max([Items]) for RowN in([1],[2],[3])) p --Pivot for getting all data
The following should give you the requested results.
Note: This solution relies on the use of an iTVF called tfn_Tally, I'll post the code for that below my answer.
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Id INT NOT NULL,
SomeName VARCHAR(3) NOT NULL,
ParentId INT NULL
);
INSERT #TestData (Id, SomeName, ParentId) VALUES
(1, 'O', NULL),
(2, 'D1', 1),
(3, 'D2', 1),
(4, 'S1', 2),
(5, 'S2', 2),
(6, 'S1', 3),
(7, 'SP1', 3);
-- SELECT * FROM #TestData td;
--===========================================
--===========================================
IF OBJECT_ID('tempdb..#RecursionResults', 'U') IS NOT NULL
DROP TABLE #RecursionResults;
WITH
cte_Recursion AS (
SELECT
td.Id,
SomeName = CAST(CAST(td.SomeName AS BINARY(5)) AS VARBINARY(1000)),
--td.ParentId,
NodeLevel = 1
FROM
#TestData td
WHERE
td.ParentId IS NULL
UNION ALL
SELECT
td.Id,
SomeName = CAST(CONCAT(r.SomeName, CAST(td.SomeName AS BINARY(5))) AS VARBINARY(1000)),
NodeLevel = r.NodeLevel + 1
FROM
cte_Recursion r
JOIN #TestData td
ON r.Id = td.ParentId
)
SELECT
Id = ISNULL(r.Id, 0), r.SomeName, r.NodeLevel
INTO #RecursionResults
FROM
cte_Recursion r;
-- adding a clustered index Id eliminates the sort operation on final select.
ALTER TABLE #RecursionResults ADD PRIMARY KEY CLUSTERED (Id);
-----------------------------------------
DECLARE
#PivotCount INT = (SELECT MAX(rr.NodeLevel) FROM #RecursionResults rr),
#PivotCols VARCHAR(1000) = '',
#PivotCAV VARCHAR(8000) = '',
#sql VARCHAR(8000),
#Debug BIT = 0; -- set to 0 to execute, 1 to DeBug.
SELECT TOP (#PivotCount)
#PivotCols = CONCAT(#PivotCols, CHAR(13), CHAR(10), CHAR(9), 'L', t.n, '.Level_', t.n, ','),
#PivotCAV = CONCAT(#PivotCAV, CHAR(13), CHAR(10), CHAR(9), 'CROSS APPLY ( VALUES (CAST(SUBSTRING(rr.SomeName,', (t.n - 1) * 5 + 1, ', ', 5, ') AS VARCHAR(5))) ) L', t.n, ' (Level_', t.n, ')'
)
FROM
dbo.tfn_Tally(#PivotCount, 1) t;
SET #sql = CONCAT('
SELECT ',
STUFF(#PivotCols, 1, 1, ''), '
rr.Id
FROM
#RecursionResults rr',
#PivotCAV, '
ORDER BY
rr.Id;');
IF #Debug = 1
BEGIN
PRINT(#sql);
END;
ELSE
BEGIN
EXEC (#sql);
END;
Function code for tfn_Tally...
CREATE FUNCTION dbo.tfn_Tally
/* ============================================================================
07/20/2017 JL, Created. Capable of creating a sequense of rows
ranging from -10,000,000,000,000,000 to 10,000,000,000,000,000
============================================================================ */
(
#NumOfRows BIGINT,
#StartWith BIGINT
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), -- 10 rows
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b), -- 100 rows
cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b), -- 10,000 rows
cte_n4 (n) AS (SELECT 1 FROM cte_n3 a CROSS JOIN cte_n3 b), -- 100,000,000 rows
cte_Tally (n) AS (
SELECT TOP (#NumOfRows)
(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) + #StartWith
FROM
cte_n4 a CROSS JOIN cte_n4 b -- 10,000,000,000,000,000 rows
)
SELECT
t.n
FROM
cte_Tally t;
GO
HTH,
Jason

Retrieve rows when values of one column changes at least once for another column value

I have a table like below in SQL Server:
DECLARE #tbl TABLE (val1 VARCHAR(10), val2 VARCHAR(10) )
INSERT INTO #tbl
VALUES ('x', 'a'), ('x', 'a'), ('p', 'b'), ('y', 'a'), ('p', 'b');
val1 | val2
------+-------
x | a
x | a
p | b
y | a
p | b
The result should be rows of a (of val2) only as value of a in val1 changes to y at least once. But the rows of val2.b should be ignored in the result as its value in val1 does not change.
val1 | val2
------+------
x | a
x | a
y | a
Try this,
select t.*
from #tbl t
INNER JOIN ( select t2.val2
from #tbl t2
group by t2.val2
having min(t2.val1) <> max(t2.val1)
) AS tt ON tt.val2 = t.val2
I have finally end up using like below.
SELECT t.*
FROM #tbl t
WHERE EXISTS (
SELECT t2.val2
FROM #tbl t2
WHERE t.val2 = t2.val2
GROUP BY t2.val2
HAVING min(t2.val1) <> max(t2.val1)
)
SELECT
*
FROM #tbl
WHERE val2 IN
(
SELECT
Z.val2
FROM
(
SELECT
*,ROW_NUMBER() OVER(Partition BY val2 Order BY Val2) AS PartNo
FROM
(
SELECT
*
FROM #tbl
GROUP BY val1,val2
)X
)Z WHERE Z.PartNo>1
)
Does this do what you want?
select t.*
from #tbl t
where t.val2 = (select t2.val2
from #tbl t2
group by t2.val2
having min(t2.val1) <> max(t2.val1)
);

Concatenate every 2 rows in a single column table

This is the code I used to concatenate the first two rows which each have 6 characters each. Each row of data is set at 6.
My problem is it only returns the first concatenate needed and doesn't effect the next rows.
DECLARE #r_strands VARCHAR(MAX)
SET #r_strands=''
SELECT #r_strands= #r_strands + R_Strands
FROM rs_table
SELECT LEFT(#r_strands, +12) AS text
rs_table
r_strands
thedog
wentto
hisbed
wherei
placed
foodto
eatfor
supper
the above is an example of the single column table I want to concat every two rows
EX. result desired
r_strands
thedogwentto
hisbedwherei
placedfoodto
eatforsupper
You can use ROW_NUMBER to make pairs of two consecutive r_strands. Then use FOR XML PATH('') for concatenation:
If you have an Id to determine the order, you can replace the ORDER BY with Id instead of SELECT NULL.
SQL Fiddle
;WITH Cte AS(
SELECT *,
RN = (ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + 1) / 2
FROM rs_table
)
SELECT
r_strands = (
SELECT '' + r_strands
FROM Cte
WHERE RN = c.RN
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'
)
FROM Cte c
GROUP BY RN
RESULT:
| r_strands |
|--------------|
| thedogwentto |
| hisbedwherei |
| placedfoodto |
| eatforsupper |
Supposing that you have an row number column (call it "id"), you can self join adjacent columns on the id, and concatenate the strings using the + string concatination operator:
select a.r_strands + b.r_strands
from rs_table a
join rs_table b
on a.id = b.id - 1
declare #t table (id int, r_strands varchar(100));
insert into #t values
(1, 'thedog'),
(2, 'wentto'),
(3, 'hisbed'),
(4, 'wherei'),
(5, 'placed'),
(6, 'foodto'),
(7, 'eatfor'),
(8, 'supper');
select r_strands + next_row
from
(
select *,
next_row = lead(r_strands) over(order by id),
is_mod = id % 2
from #t
) x
where is_mod != 0;

Select alternate rows from SQL Server table

I am working with SQL Server 2008. I have a table which does not contain any unique columns; how to get alternate rows from it?
SQL Server table:
+-----+--------+
| id | name |
|-----+--------|
| 1 | abc |
| 2 | pqr |
| 2 | pqr |
| 3 | xyz |
| 4 | lmn |
| 5 | efg |
| 5 | efg |
+-----+--------+
As we've to come with at least one working suggestion with the question, I've tried below code; which is not so proper technique when fetching from a huge amount of data.
Trial:
create table #tmp
(
id int, name varchar(10), srNo int
)
insert into #tmp
select
id, name,
ROW_NUMBER() OVER (ORDER BY id) % 2 as srNo --,alternate rows
from
Employee
select *
from #tmp
where srNo = 1 --or srNo = 0
Above query gives out alternate rows i.e. 1st, 3rd, 5th OR 2nd, 4th, 6th etc.
Please help me out with proper way without #tmp to achieve the goal!
You can just use your select statement as an in-line view. You don't need the #tmp table.
select t.id, name
from (select id, name, ROW_NUMBER() over (order by id) as srNo from Employee) t
where (t.srNo % 2) = 1
SqlFiddle
--To fetch ALTERNATE records from a table (EVEN NUMBERED)
Select * from TableName where ColumnName % 2 = 0
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 0
--To fetch ALTERNATE records from a table (ODD NUMBERED)
Select * from TableName where ColumnName % 2 = 1
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 1
I'm taking student as a table name.
Here is my answer ->
For Even Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=0
For Odd Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=1
Same also can be achieved using having clause; but it adds group by task:
SELECT id, name
FROM (SELECT id, name, ROW_NUMBER()over(order by id) AS srNo FROM Employee) x
GROUP BY srNo, id, name
HAVING (srNo % 2) = 0
You can just use your select statement as an in-line view. You don't need the #tblCities table.
select tbl1.CityID,tbl1.CityName from (select ROW_NUMBER() over(order by CityID asc) as row_no,CityID,CityName from tblCities) as tbl1 where tbl1.row_no%2=1
declare #t table
(
id int,
name nvarchar(20)
)
insert into #t
Select 1, 'abc'
union all
Select 2, 'pqr'
union all
Select 2, 'pqr'
union all
Select 3, 'xyz'
union all
Select 4, 'lmn'
union all
Select 5, 'efg'
union all
Select 2, 'efg'
Select * from(
Select *, row_number() over(order by id) as rnum from #t ) t where rnum % 2 <> 0
create table t (id bigint NOT NULL, input_1 boolean not null, data_gps timestamp(0) not null);
insert into t (id, input_1,data_gps) values
(1, false , '2022-01-01 15:42:07'),
(2, true , '2022-01-02 15:42:07'),
(3, true , '2022-01-03 15:42:07'),
(4, false , '2022-01-04 15:42:07'),
(5, true , '2022-01-05 15:42:07'),
(6, true , '2022-01-06 15:42:07'),
(7, true , '2022-01-07 15:42:07'),
(8, true , '2022-01-08 15:42:07'),
(9, false , '2022-01-09 15:42:07'),
(10 ,true , '2022-01-10 15:42:07'),
(11, true , '2022-01-11 15:42:07'),
(12, true , '2022-01-12 15:42:07'),
(13, false , '2022-01-13 15:42:07'),
(14, true , '2022-01-14 15:42:07');
you will have
Here is the query that will group by value change
select input_1, min(data_gps) as mind, max(data_gps) as maxd
from (
select input_1, data_gps,
row_number() over (order by data_gps)
- row_number() over (partition by input_1 order by data_gps) as grp
from t
) as tmp
group by input_1, grp
order by min(data_gps);
The results
DEMO
https://dbfiddle.uk/6Ajy3H5O

COUNT number of rows in a GROUP on higher aggregate level

I am trying to find out how many rows of a certain item exist in the table, e.g. in the following example for itemID 1 I need the result 5 (not 3, which is what I currently get). I am tempted to add TransactionID into the PARTITION BY clause, but that results in Msg 8120 since the query does not GROUP by TransactionID. Well, if it did then getting that count would be easy, but I do not want to group on Transaction Level. What can I do to get that ItemCount right? It must be so easy but I am banging my head.
DECLARE #t TABLE (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO #t (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
SELECT
CustomerID,
ItemID,
Rows = COUNT(*),
ItemRowCount = COUNT(*) OVER (PARTITION BY ItemID)
FROM
#t
GROUP BY
CustomerID,
ItemID
ORDER BY
ItemID,
CustomerID;
EDIT: I was overaggregating, I guess. Sebastian Meine got me on the track and his answer is right so I accepted it. However, this subquery works for my:
SELECT
CustomerID,
ItemID,
Rows = COUNT(*),
ItemRowCount = (SELECT COUNT(*) FROM #t x WHERE t.ItemID = x.ItemID)
FROM
#t t
GROUP BY
CustomerID,
ItemID
ORDER BY
ItemID,
CustomerID;
You need to pull your outer group count out of the actual group by query. The easiest way to do that is like this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.tbl (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO dbo.tbl (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
Query 1:
SELECT *,SUM(Rows)OVER(PARTITION BY ItemId) ItemCnt
FROM(
SELECT
CustomerID,
ItemID,
Rows = COUNT(*)
FROM
dbo.tbl
GROUP BY
CustomerID,
ItemID
)X
ORDER BY
ItemID,
CustomerID
Results:
| CUSTOMERID | ITEMID | ROWS | ITEMCNT |
----------------------------------------
| 1 | 1 | 3 | 5 |
| 2 | 1 | 1 | 5 |
| 3 | 1 | 1 | 5 |
| 2 | 2 | 1 | 2 |
| 4 | 2 | 1 | 2 |
| 3 | 3 | 1 | 1 |
| 4 | 4 | 1 | 1 |
Notice that I add the inner counts together instead of recounting from scratch.
You can use simple count and group by:
SELECT
ItemID,
ItemRowCount = COUNT(1)
FROM
#t
GROUP BY
ItemID
ORDER BY
ItemID
or if you need attach total rows count and item row count to every row:
SELECT
CustomerID,
ItemID,
Rows = COUNT(1) over (),
ItemRowCount = COUNT(1) OVER (PARTITION BY ItemID)
FROM
#t
ORDER BY
ItemID,
CustomerID;
To find out how many rows of a certain item exist in the table, we may not need CustomerId. Use following query -
DECLARE #t TABLE (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO #t (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
;WITH cte AS(
SELECT itemId, ROW_NUMBER() OVER (PARTITION BY itemId ORDER BY itemId DESC) AS row_cnt FROM #t
)
SELECT itemId, MAX(row_cnt) row_count FROM cte GROUP BY itemId
It will return -
itemId row_count
1 5
2 2
3 1
4 1
And if is case you need customerId, the use -
;WITH cte AS(
SELECT customerId, itemId, ROW_NUMBER() OVER (PARTITION BY itemId ORDER BY itemId DESC) AS row_cnt FROM #t
)
SELECT customerId, itemId, MAX(row_cnt) item_count FROM cte GROUP BY CustomerID, itemId
It will return -
customerId itemId item_count
1 1 5
2 1 2
3 1 3
2 2 1
4 2 2
3 3 1
4 4 1
You can use a CTE or join to a sub-table (like this)
SELECT
tbl.CustomerID,
tbl.ItemID,
Rows = COUNT(*),
ItemRowCount
FROM tbl
JOIN (SELECT ItemID, Count(*) as ItemRowCount
FROM tbl
GROUP BY ItemID) t ON tbl.ItemID = t.ItemID
GROUP BY
tbl.CustomerID,
tbl.ItemID,
ItemRowCount
ORDER BY
tbl.ItemID,
CustomerID;
or this
SELECT
tbl.CustomerID,
tbl.ItemID,
Rows = COUNT(*),
MAX(ItemRowCount)
FROM tbl
JOIN (SELECT ItemID, Count(*) as ItemRowCount
FROM tbl
GROUP BY ItemID) t ON tbl.ItemID = t.ItemID
GROUP BY
tbl.CustomerID,
tbl.ItemID
ORDER BY
tbl.ItemID,
CustomerID;

Resources