SQL server, how to get a number of distinct items - sql

I am using SQL server,
id 3 | 4 | 5 | 6
items 1 2 3 | 2 3 5| 6 | 1 2 5
-------------------------
# of items 3 | 4 | 5 | 5
so, each id has items (ex, 3 has 3 items - 1,2,3)
and for each item, I'd like to get the number of distinct items accrued.
so, 3 has 3 distinct items - 1, 2, 3
4 has 4 distinct items - 1, 2, 3, 5
5 has 5 distinc items - 1, 2, 3, 5, 6
6 has 5 distinct items - 1, 2, 3, 5, 6
I can do this by running, 1 through 2, 1 though 3, 1 through 5 and 1 through 6 by doing count(distinct items)
But I want to automate this process and get the same results in one run.
The idea is to create a temp table and put an item in it while checking if the item is already in the temp table and print number of distinct items for each id.

CREATE TABLE TEST
(
id int, items int
)
INSERT INTO TEST
VALUES
(3, 1),
(3, 2),
(3, 3),
(4, 2),
(4, 3),
(4, 5),
(5, 6),
(6, 1),
(6, 2),
(6, 5)
SELECT B.id, COUNT(DISTINCT(A.ITEMS)) AS itemCount
FROM TEST A
INNER JOIN TEST B ON A.id <= B.id
GROUP BY B.ID
DROP TABLE TEST
Output:
id itemCount
3 3
4 4
5 5
6 5

Assuming your data in below format:
Declare #table table
(
id int,
items varchar(10)
)
insert into #table values (3, '1 2 3');
insert into #table values (4, '2 3 5');
insert into #table values (5, '6');
insert into #table values (6, '1 2 5');
with cte as
(
Select id, b.Item
from #table a
cross apply [dbo].[Split] (items, ' ') b
)
Select y.id, count(distinct(x.Item)) AS [# of items]
from cte x
join cte y on x.id <= y.id
group by y.id
Use the table valued function [dbo].[Split] from LINK.

You can as the below:
DECLARE #Tbl TABLE (Id VARCHAR(10), Column3 VARCHAR(100), Column4 VARCHAR(100), Column5 VARCHAR(100), Column6 VARCHAR(100))
INSERT #Tbl
VALUES
('items', '1 2 3', '2 3 5', '6', '1 2 5')
;WITH CTE1
AS
(
SELECT T.Id, T.Column3 AS ColumnId, CAST('<X>' + REPLACE(T.Column3,' ','</X><X>') + '</X>' as XML) AS FilterColumn FROM #Tbl T UNION ALL
SELECT T.Id, T.Column4 AS ColumnId, CAST('<X>' + REPLACE(T.Column4,' ','</X><X>') + '</X>' as XML) AS FilterColumn FROM #Tbl T UNION ALL
SELECT T.Id, T.Column5 AS ColumnId, CAST('<X>' + REPLACE(T.Column5,' ','</X><X>') + '</X>' as XML) AS FilterColumn FROM #Tbl T UNION ALL
SELECT T.Id, T.Column6 AS ColumnId, CAST('<X>' + REPLACE(T.Column6,' ','</X><X>') + '</X>' as XML) AS FilterColumn FROM #Tbl T
), CTE2
AS
(
SELECT
A.*,
B.SplitData
FROM
CTE1 A CROSS APPLY
(SELECT fdata.D.value('.','varchar(50)') AS SplitData FROM A.FilterColumn.nodes('X') as fdata(D)) B
)
SELECT
T.Id ,
(SELECT COUNT(DISTINCT C.SplitData) FROM CTE2 C WHERE C.Id = T.Id AND C.ColumnId IN (T.Column3)) Column3OfDistinct,
(SELECT COUNT(DISTINCT C.SplitData) FROM CTE2 C WHERE C.Id = T.Id AND C.ColumnId IN (T.Column3, T.Column4)) Column4OfDistinct,
(SELECT COUNT(DISTINCT C.SplitData) FROM CTE2 C WHERE C.Id = T.Id AND C.ColumnId IN (T.Column3, T.Column4, T.Column5)) Column5OfDistinct,
(SELECT COUNT(DISTINCT C.SplitData) FROM CTE2 C WHERE C.Id = T.Id AND C.ColumnId IN (T.Column3, T.Column4, T.Column5, T.Column6)) Column6OfDistinct
FROM
#Tbl T
Result:
Id Column3OfDistinct Column4OfDistinct Column5OfDistinct Column6OfDistinct
---------- ----------------- ----------------- ----------------- -----------------
items 3 4 5 5

This should help you:
select
id,
count(items)
from table_name
group by id

Related

Sql Server 2014 - Deep Recursive Parent-Child Self Join

I am trying to build a deep recursive self-join query. Having the table like:
Id | ParentId
1 | NULL
2 | 1
3 | 1
4 | 2
5 | 3
6 | 8
7 | 9
For Id 1 my query should be fetching 1,2,3,4,5 since they are either the children of 1 or children of the children of 1. In the given example 6 and 7 should not be included in the query result.
I tried using CTE but I am getting tons of duplicates:
WITH CTE AS (
SELECT Id, ParentId
FROM dbo.Table
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM dbo.Table t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT * FROM CTE
Ideas?
You can try to use DISTINCT to filter duplicate rows.
;WITH CTE AS (
SELECT Id, ParentId
FROM T
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM T
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT DISTINCT Id, ParentId
FROM CTE
Try the following query with CTE where you can set parentId by #parentID:
DECLARE #parentID INT = 1
;WITH cte AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
),
cteParent AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
WHERE t.ParentId IN (SELECT t1.ID FROM #table t1 WHERE T1.ParentId = #parentID)
)
SELECT
DISTINCT c1.ID
, c1.ParentId
FROM cte c1
INNER JOIN cte c2 ON c2.ParentId = c1.ID
UNION ALL
SELECT *
FROM cteParent
And the sample data:
DECLARE #table TABLE
(
ID INT
, ParentId INT
)
INSERT INTO #table
(
ID,
ParentId
)
VALUES
(1, NULL )
, (2, 1 )
, (3, 1 )
, (4, 2 )
, (5, 3 )
, (6, 8 )
, (7, 9 )
OUTPUT:
ID ParentId
1 NULL
2 1
3 1
4 2
5 3
I don't see duplicates.
Your code returns the following on the data you provided:
Id ParentId
1
2 1
3 1
5 3
4 2
which is what you want.
Here is a db<>fiddle.
Here is the code:
WITH t as (
SELECT *
FROM (VALUES (1, NULL), (2, 1), (3, 1), (4, 2), (5, 3), (6, 8), (7, 9)
) v(id, parentId)
),
CTE AS (
SELECT Id, ParentId
FROM t
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT *
FROM CTE;
If you are getting duplicates in your actual result set, then you presumably have duplicates in your original table. I would recommend removing them before doing the recursive logic:
with t as (
select distinct id, parentid
from <your query>
),
. . .
Then run the recursive logic.
Try this sql script which result Parent Child Hierarchy
;WITH CTE(Id , ParentId)
AS
(
SELECT 1 , NULL UNION ALL
SELECT 2 , 1 UNION ALL
SELECT 3 , 1 UNION ALL
SELECT 4 , 2 UNION ALL
SELECT 5 , 3 UNION ALL
SELECT 6 , 8 UNION ALL
SELECT 7 , 9
)
,Cte2
AS
(
SELECT Id ,
ParentId ,
CAST('\'+ CAST(Id AS VARCHAR(MAX))AS VARCHAR(MAX)) AS [Hierarchy]
FROM CTE
WHERE ParentId IS NULL
UNION ALL
SELECT c1.Id ,
c1.ParentId ,
[Hierarchy]+'\'+ CAST(c1.Id AS VARCHAR(MAX)) AS [Hierarchy]
FROM Cte2 c2
INNER JOIN CTE c1
ON c1.ParentId = c2.Id
)
SELECT Id,
RIGHT([Hierarchy],LEN([Hierarchy])-1) AS ParentChildHierarchy
FROM Cte2
GO
Result
Id ParentChildHierarchy
-------------------------
1 1
2 1\2
3 1\3
5 1\3\5
4 1\2\4
This query will help you
CREATE TABLE #table( ID INT, ParentId INT )
INSERT INTO #table(ID,ParentId)
VALUES (1, NULL ), (2, 1 ), (3, 1 ), (4, 2 ), (5, 3 ), (6, 8 ), (7, 9 )
;WITH CTE AS (
SELECT ID FROM #table WHERE PARENTID IS NULL
UNION ALL
SELECT T.ID FROM #table T
INNER JOIN #table T1 ON T.PARENTID =T1.ID
) SELECT * FROM CTE

Update table in Sql

Table tblProductStock:
ID Name Qyt
1 X 50
2 Y 40
3 Z 30
Table tblStockMinus:
Id Name Qty
1 X 10
1 X 20
2 Y 30
3 Z 20
I want to update tblProductStock so that it will have:
ID Name Qyt
1 X 20
2 Y 10
3 Z 10
I have tried to use the following SQL request, but it didn't work correctly:
UPDATE ProductStock_Show
SET Quantity = ProductStock_Show.Quantity - ProductStock_Show_Minus.Quantity
FROM ProductStock_Show JOIN ProductStock_Show_Minus
ON ProductStock_Show_Minus.ProductId=ProductStock_Show.ProductId
This will give you required result.
select t1.id, t1.qty-t2.qty as qty from tblProductStock as t1 inner join
(
select id, sum(qty) as qty from tblStockMinus group by id
) as t2 on t1.id=t2.id
You need to update this (but it depends in the RDBMS. This is for MS SQL Server)
Update t
set
t.qty=t3.qty from tblProductStock as t inner join
(
select t1.id, t1.qty-t2.qty as qty from tblProductStock as t1 inner join
(
select id, sum(qty) as qty from tblStockMinus group by id
) as t2 on t1.id=t2.id
) as t3 on t.id=t3.id
DECLARE #Table1 TABLE
( ID int, Name varchar(1), Qyt int)
;
INSERT INTO #Table1
( ID , Name , Qyt )
VALUES
(1, 'X', 50),
(2, 'Y', 40),
(3, 'Z', 30)
;
DECLARE #Table2 TABLE
( Id int, Name varchar(1), Qty int)
;
INSERT INTO #Table2
( Id , Name , Qty )
VALUES
(1, 'X', 10),
(1, 'X', 20),
(2, 'Y', 30),
(3, 'Z', 20)
;
UPDATE #Table1 SET QYT =
t.Qyt - s from (
select
t.id,
t.Name,
t.Qyt ,
SUM(tt.Qty)S
FROM #Table1 t
INNER JOIN #Table2 tt
ON t.Name = tt.Name
GROUP BY t.id,t.Name,t.Qyt)T
SELECT * from #Table1

Unpivot multiple columns not showing desire result

Original
RecordKey Name Section1_Product Section1_Code Section2_Product Section2_Code ......
1 a ff 22
2 b gg 22
3 c hh 33
RecordKey Name Section Product Code ......
1 a 1 ff 22
1 a 2
2 b 1 gg 22
2 b 2
3 c 1 hh 22
3 c 2
I am trying to unpivot the columns into rows. Some sections will have null value.
SELECT RecordKey
,Name
,'Num_of_Sections' = ROW_NUMBER() OVER (PARTITION BY RecordKey ORDER BY ID)
,Product
,Code
FROM (
SELECT RecordKey, Name, Section1_Product, Section1_Code, Section2_Product, Section2_Code FROM Table
) M
UNPITVOT (
Product FOR ID IN (Section1_Product, Section2_Product)
) p
UNPIVOT (
Code FOR CO IN (Section1_Code, Section2_Code)
) c
If I execute with only one column (Product, comment out Code) then I will have 2 values in ID column (1,2). If I run the query with 2 columns then I get 4 values in ID column(1, 2, 3, 4).
may as per my assumption and your data provided we can achieve this using Cross apply and Row_number
declare #Record TABLE
([RecordKey] int,
[Name] varchar(1),
[Section1_Product] varchar(2),
[Section1_Code] int,
[Section2_Product] varchar(2),
[Section2_Code] int)
;
INSERT INTO #Record
([RecordKey], [Name], [Section1_Product], [Section1_Code],[Section2_Product],[Section2_Code])
VALUES
(1, 'a', 'ff', 22,NULL,NULL),
(2, 'b', 'gg', 22,NULL,NULL),
(3, 'c', 'hh', 33,NULL,NULL)
;
With cte as (
Select T.RecordKey,
T.Name,
T.val,
T.val1 from (
select RecordKey,Name,val,val1 from #Record
CROSS APPLY (VALUES
('Section1_Product',Section1_Product),
('Section2_Product',Section2_Product))cs(col,val)
CROSS APPLY (VALUES
('Section1_Code',Section1_Code),
('Section2_Code',Section2_Code))css(col1,val1)
WHERE val is NOT NULL)T
)
Select c.RecordKey,
c.Name,
c.RN,
CASE WHEN RN = 2 THEN NULL ELSE c.val END Product,
c.val1 Code
from (
Select RecordKey,
Name,
ROW_NUMBER()OVER(PARTITION BY val ORDER BY (SELECT NULL))RN,
val,
val1 from cte )C

Need a query to insert 'level' into an adjacent list

I have a table like so
ID Node ParentID
1 A 0
2 B 1
3 C 1
4 D 2
5 E 2
6 F 3
7 G 3
8 H 3
9 I 4
10 J 4
11 K 10
12 L 11
I need a query to generate a 'level' field that shows how many levels deep a particular node is. Example below
ID Node ParentID Level
1 A 0 1
2 B 1 2
3 C 1 2
4 D 2 3
5 E 2 3
6 F 3 4
7 G 3 4
8 H 3 4
9 I 4 5
10 J 4 5
11 K 10 6
12 L 11 7
Select Id,
Node,
ParentID,
Dense_Rank() Over(Order by ParentID) as Level
from Table_Name
SQL Fiddle Demo
You can use DENSE_RANK function
SELECT i.ID, p.Node, i.ParentID
,Dense_Rank() Over(Order by ParentID) as Level
FROM TableName AS i;
for more detail visit: http://blog.sqlauthority.com/2007/10/09/sql-server-2005-sample-example-of-ranking-functions-row_number-rank-dense_rank-ntile/
I think the correct way to do it will be to get the parent level and increment it by 1 when inserting the data since all other ways are expensive performance wise.
Something like:
;with tree (ID, ParentID, Level)
as (
select ID, ParentID, 1 from TableName where ParentID = 0
union all
select t.ID, t.ParentID, 1 + tree.Level
from Tree join TableName t on t.ParentID = Tree.ID
)
select ID, Level from Tree
Try this
CREATE TABLE #Table1
([ID] int, [Node] varchar(1), [ParentID] int)
;
INSERT INTO #Table1
([ID], [Node], [ParentID])
VALUES
(1, 'A', 0),
(2, 'B', 1),
(3, 'C', 1),
(4, 'D', 2),
(5, 'E', 2),
(6, 'F', 3),
(7, 'G', 3),
(8, 'H', 3),
(9, 'I', 4),
(10, 'J', 4),
(11, 'K', 10),
(12, 'L', 11)
;
;WITH CTE ([ID], [ParentID], [Node], [Level])
as (
SELECT [ID], [ParentID], [Node], 1 FROM #Table1 WHERE ParentID = 0
UNION all
select t.[ID], t.[ParentID], t.[Node], 1 + c.[Level]
from CTE c inner join #Table1 t ON t.[ParentID] = c.[ID]
)
select ID, [Node], [ParentID], [Level] from CTE
ORDER BY [Node]
DROP TABLE #Table1
Here, you need to set level by grouping ParentID then join both tables by ParentID.
WITH CTE (ParentID, Level)
AS (
SELECT ParentID
, Row_Number() OVER (ORDER BY ParentID) AS Level
FROM Table1
GROUP BY ParentID
)
SELECT t1.ID, t1.Node, t1.ParentID, CTE.Level
FROM Table1 t1
JOIN CTE ON t1.ParentID = CTE.ParentID;
See this SQLFiddle
Update: (for MySQL - just to help others)
To do the same in MySQL try to get row number like this:
SELECT t1.ID, t1.Node, t1.ParentID, Tbl.Level
FROM Table1 t1
JOIN
(
SELECT #Level:=#Level+1 AS Level , ParentID
FROM (SELECT DISTINCT ParentID FROM Table1) t
, (SELECT #Level:=0) r
ORDER BY ParentID
) Tbl
ON t1.ParentID = Tbl.ParentID;
See this SQLFiddle

Consolidating subsets in a table

I have a table in SqlServer 2008 with data of the form
UserID StartWeek EndWeek Type
1 1 3 A
1 4 5 A
1 6 10 A
1 11 13 B
1 14 16 A
2 1 5 A
2 6 9 A
2 10 16 B
I'd like to consolidate/condense the adjacent types so that the resulting table looks like this.
UserID StartWeek EndWeek Type
1 1 10 A
1 11 13 B
1 14 16 A
2 1 9 A
2 10 16 B
Does anyone have any suggestions as to the best way to accomplish this? I've been looking at using Row_number and Partition, but I can't get it to behave exactly as I'd like.
There's probably a neater way to do it, but this produces the correct result
DECLARE #t TABLE
(UserId TINYINT
,StartWeek TINYINT
,EndWeek TINYINT
,TYPE CHAR(1)
)
INSERT #t
SELECT 1,1,3,'A'
UNION SELECT 1,4,5,'A'
UNION SELECT 1,6,10,'A'
UNION SELECT 1,11,13,'B'
UNION SELECT 1,14,16,'A'
UNION SELECT 2,1,5,'A'
UNION SELECT 2,6,9,'A'
UNION SELECT 2,10,16,'B'
;WITH srcCTE
AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY t1.UserID, t1.Type
ORDER BY t1.EndWeek
) AS rn
FROM #t AS t1
)
,recCTE
AS
(
SELECT *
,0 AS grp
FROM srcCTE
WHERE rn = 1
UNION ALL
SELECT s.UserId
,s.StartWeek
,s.EndWeek
,s.TYPE
,s.rn
,CASE WHEN s.StartWeek - 1 = r.EndWeek
THEN r.grp
ELSE r.grp+ 1
END AS GRP
FROM srcCTE AS s
JOIN recCTE AS r
ON r.UserId = s.UserId
AND r.TYPE = s.TYPE
AND r.rn = s.rn - 1
)
SELECT UserId
,MIN(StartWeek) AS StartWeek
,MAX(EndWeek) AS EndWeek
,TYPE
FROM recCTE AS s1
GROUP BY UserId
,TYPE
,grp
Also using a CTE, but in a slightly different way
DECLARE #Consolidate TABLE (
UserID INTEGER, StartWeek INTEGER,
EndWeek INTEGER, Type CHAR(1))
INSERT INTO #Consolidate VALUES (1, 1, 3, 'A')
INSERT INTO #Consolidate VALUES (1, 4, 5, 'A')
INSERT INTO #Consolidate VALUES (1, 6, 10, 'A')
INSERT INTO #Consolidate VALUES (1, 14, 16, 'A')
INSERT INTO #Consolidate VALUES (1, 11, 13, 'B')
INSERT INTO #Consolidate VALUES (2, 1, 5, 'A')
INSERT INTO #Consolidate VALUES (2, 6, 9, 'A')
INSERT INTO #Consolidate VALUES (2, 10, 16, 'B')
;WITH ConsolidateCTE AS
(
SELECT UserID, StartWeek, EndWeek, Type
FROM #Consolidate
UNION ALL
SELECT cte.UserID, cte.StartWeek, c.EndWeek, c.Type
FROM ConsolidateCTE cte
INNER JOIN #Consolidate c ON
c.UserID = cte.UserID
AND c.StartWeek = cte.EndWeek + 1
AND c.Type = cte.Type
)
SELECT UserID, [StartWeek] = MIN(Startweek), EndWeek, Type
FROM (
SELECT UserID, Startweek, [EndWeek] = MAX(EndWeek), Type
FROM ConsolidateCTE
GROUP BY UserID, StartWeek, Type
) c
GROUP BY UserID, EndWeek, Type
ORDER BY 1, 2, 3