Aggregate count of different columns - sql-server

There are two table:
Table a:
id name b_id1 b_id2 b_id3 b_id4
1 a 10 8 null null
2 b 3 4 8 10
3 c 10 5 4 null
Table B
b_id title
3 Value3
4 Value4
5 Value 5
8 Value 8
10 Value
Table A has F.K on b_id1,b_id2,b_id3,b_id4 to table B on b_id cloumn,
We are going to group on Title and count it.
something like following result:
Title Count
Value3 1
Value4 2
Value5 1
Value8 2
Value10 3
Null 3

Full working example:
DECLARE #Table1 TABLE
(
[id] INT
,[name] VARCHAR(1)
,[b_id1] INT
,[b_id2] INT
,[b_id3] INT
,[b_id4] INT
);
DECLARE #Table2 TABLE
(
[b_id] INT
,[title] VARCHAR(7)
);
INSERT INTO #Table1 ([id], [name], [b_id1], [b_id2], [b_id3], [b_id4])
VALUES (1, 'a', 10, 8, NULL, NULL)
,(2, 'b', 3, 4, 8, 10)
,(3, 'c', 10, 5, 4, NULL);
INSERT INTO #Table2 ([b_id], [title])
VALUES (3, 'Value3')
,(4, 'Value4')
,(5, 'Value 5')
,(8, 'Value 8')
,(10, 'Value');
SELECT T2.[title]
,COUNT(*)
FROM
(
SELECT [b_id1]
FROM #Table1
UNION ALL
SELECT [b_id2]
FROM #Table1
UNION ALL
SELECT [b_id3]
FROM #Table1
UNION ALL
SELECT [b_id4]
FROM #Table1
) DS
LEFT JOIN #Table2 T2
ON DS.[b_id1] = T2.[b_id]
GROUP BY T2.[title];

One way would be like below:Demo Here
;with cte
as
(select title,count(b_id) as val from table1 t
join
table2 t2
on t.id=t2.b_id
or
t.b_id1=t2.b_id
or
t.b_id2=t2.b_id
or
t.b_id3=t2.b_id
or
t.b_id4=t2.b_id
group by title
)
select * from cte
union all
select null,sum(case
when b_id1 is null then 1 else 0 end)+
sum(case
when b_id2 is null then 1 else 0 end)
+sum(case
when b_id3 is null then 1 else 0 end)
+sum(case
when b_id4 is null then 1 else 0 end)
from table1
output:
Value 3
Value 5 1
Value 8 2
Value3 2
Value4 2
NULL 3

Related

Add incrementally row values of a column of type string

I have a table with the following values
UserID ParentID Levels Path
1 NULL 0 A1
5 1 1 A2
9 5 2 A3
43 9 3 A4
The output should be like followed :
UserID ParentID Levels FinalPath
1 NULL 0 A1/
5 1 1 A1/A2/
9 5 2 A1/A2/A3/
43 9 3 A1/A2/A3/A4/
Thanks in advance for any guidance on this.
Solution using a recusive common table expression.
Sample data
create table users
(
userid int,
parentid int,
levels int,
path nvarchar(100)
);
insert into users (userid, parentid, levels, path) values
(1, NULL, 0, 'A1'),
(5, 1, 1, 'A2'),
(9, 5, 2, 'A3'),
(43, 9, 3, 'A4');
Solution
with cte as
(
select u.userid, u.parentid, u.levels, u.path
from users u
where u.parentid is null
union all
select u.userid, u.parentid, u.levels, convert(nvarchar(100), c.path + '/' + u.path)
from users u
join cte c
on c.userid = u.parentid
)
select c.userid, c.parentid, c.levels, c.path + '/' as FinalPath
from cte c;
Fiddle
Here's a version that both calculates the Level and appends the Path.
Data
drop table if exists dbo.test_table;
go
create table dbo.test_table(
UserID int,
ParentID int,
[Path] varchar(5));
insert dbo.test_table([UserID], [ParentID], [Path]) values
(1,null, 'A1'),
(5,1, 'A2'),
(9,5, 'A3'),
(43,9, 'A4');
Query
;with recur_cte([UserId], [ParentID], h_level, [Path]) as (
select [UserId], [ParentID], 0, cast([Path] as varchar(100))
from dbo.test_table
where [ParentID] is null
union all
select tt.[UserId], tt.[ParentID], rc.h_level+1, cast(concat(tt.[Path], '/', rc.[Path]) as varchar(100))
from dbo.test_table tt join recur_cte rc on tt.[ParentID]=rc.[UserId])
select * from recur_cte;
Results
UserId ParentID h_level Path
1 NULL 0 A1
5 1 1 A1/A2
9 5 2 A1/A2/A3
43 9 3 A1/A2/A3/A4

Merge Segment Ranges Based on Grouped Value

I have a table of segments with a beginning point, an ending point, and a value like so:
Bmp | Emp | SomeVal
0 1 1
1 2 1
2 3 2
3 4 2
4 5 1
I would like to merge (summarize) these records so they look like so:
Bmp | Emp | SomeVal
0 2 1
2 4 2
4 5 1
I've simplified my data set for the purpose of this question. The end result is I need unique rows grouped by the SomeVal column (in my real data set, there are about 20 columns) with the segments stitched together from Bmp to Emp, but not overlapping.
I've tried the following:
DECLARE #tbl TABLE (Bmp int, Emp int, SomeVal int)
INSERT INTO #tbl
SELECT 0, 1, 1 UNION
SELECT 1, 2, 1 UNION
SELECT 2, 3, 2 UNION
SELECT 3, 4, 2 UNION
SELECT 4, 5, 1
SELECT MIN(Bmp) AS Bmp, Max(Emp) AS Emp, SomeVal FROM #tbl
GROUP BY SomeVal
Unfortunately, it comes out like so which is wrong:
Bmp | Emp | SomeVal
0 5 1
2 4 2
My query above only works if the values of SomeVal do not repeat. How can I fix my sql?
Minimum required version is SQL 2008.
You may using ROW_NUMBER() function to correlate begin group row with end group row.
DECLARE #tbl TABLE (Bmp int, Emp int, SomeVal int)
INSERT INTO #tbl
SELECT 0, 1, 1 UNION
SELECT 1, 2, 1 UNION
SELECT 2, 3, 2 UNION
SELECT 3, 4, 2 UNION
SELECT 4, 5, 1
;WITH
[Begins] AS
(
SELECT Bmp, SomeVal, ROW_NUMBER() OVER (ORDER BY Bmp) AS OrderNumber
FROM #tbl AS [Begin]
WHERE NOT EXISTS (
SELECT 1
FROM #tbl AS [Prev]
WHERE [Prev].Emp = [Begin].Bmp AND [Prev].SomeVal = [Begin].SomeVal)
),
[Ends] AS
(
SELECT Emp, SomeVal, ROW_NUMBER() OVER (ORDER BY Emp) AS OrderNumber
FROM #tbl AS [End]
WHERE NOT EXISTS (
SELECT 1
FROM #tbl AS [Next]
WHERE [Next].Bmp = [End].Emp AND [Next].SomeVal = [End].SomeVal)
)
SELECT [Begins].Bmp, [Ends].Emp, [Begins].SomeVal
FROM [Begins]
INNER JOIN [Ends]
ON [Begins].OrderNumber = [Ends].OrderNumber;
Here is another option...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Bmp INT NOT NULL PRIMARY KEY CLUSTERED,
Emp INT NOT NULL,
SomeVal INT NOT NULL
);
INSERT #TestData (Bmp, Emp, SomeVal)
SELECT 0, 1, 1 UNION ALL
SELECT 1, 2, 1 UNION ALL
SELECT 2, 3, 2 UNION ALL
SELECT 3, 4, 2 UNION ALL
SELECT 4, 5, 1;
--==============================================
WITH
cte_GroupStart AS (
SELECT
td.Bmp,
td.Emp,
td.SomeVal,
GroupStart = CASE WHEN td.SomeVal = LAG(td.SomeVal, 1) OVER (ORDER BY td.Bmp) THEN NULL ELSE ROW_NUMBER() OVER (ORDER BY td.Bmp) END
FROM
#TestData td
),
cte_FillGroup AS (
SELECT
gs.Bmp,
gs.Emp,
gs.SomeVal,
AggGroup = MAX(gs.GroupStart) OVER (ORDER BY gs.Bmp) -- ROWS UNBOUNDED PRECEDING is implied and should work as expected 2008
FROM
cte_GroupStart gs
)
SELECT
Bmp = MIN(fg.Bmp),
Emp = MAX(fg.Emp),
fg.SomeVal
FROM
cte_FillGroup fg
GROUP BY
fg.AggGroup,
fg.SomeVal
ORDER BY
fg.AggGroup;

Update a column with sequence numbers without using ROW_NUMBER() in SQL Server

I have a table like this and I have difficulty updating it
code descd slnum
---------------------
10 a 0
10 b 0
12 c 0
12 d 0
11 e 0
12 f 0
I have to update this table like this without using ROW_NUMBER() only by using if else loops how can I do that?
code descd slnum
----------------------
10 a 1
10 b 2
12 c 1
12 d 2
11 e 1
12 f 3
For SQL 2012+
;WITH rownum(code, descd, slnum) AS (
SELECT 10, 'a', 0
UNION SELECT 10, 'b', 0
UNION SELECT 12, 'c', 0
UNION SELECT 12, 'd', 0
UNION SELECT 11, 'e', 0
UNION SELECT 12, 'f', 0
) SELECT code, descd, COUNT(*) OVER (PARTITION BY code ORDER BY code
ROWS UNBOUNDED PRECEDING) FROM rownum o ORDER BY descd
Try this
DECLARE #Tbl TABLE(code VARCHAR(5), descd VARCHAR(5), slnum int)
INSERT INTO #Tbl
( code, descd)
VALUES
('10', 'a'),
('10', 'b'),
('12', 'c'),
('12', 'd'),
('11', 'e'),
('12', 'f')
DECLARE #TempTable TABLE(code VARCHAR(5), descd VARCHAR(5), RowId int)
DECLARE #RowId INT = 1
WHILE #RowId <= (SELECT COUNT(*) FROM #Tbl)
BEGIN
INSERT INTO #TempTable
SELECT TOP 1
T.code ,
T.descd ,
#RowId
FROM
#Tbl T LEFT JOIN
#TempTable L ON L.code = T.code AND L.descd = T.descd
WHERE
L.code IS null
ORDER BY
T.code,
T.descd
UPDATE #Tbl
SET slnum = (SELECT COUNT(1) FROM #TempTable A WHERE [#Tbl].Code = A.Code)
WHERE
[#Tbl].Code = (SELECT TOP 1 Y.code FROM #TempTable Y WHERE RowId = #RowId) AND
[#Tbl].descd = (SELECT TOP 1 Y.descd FROM #TempTable Y WHERE RowId = #RowId)
SET #RowId += 1
END
SELECT * from #Tbl
Result:
code descd slnum
10 a 1
10 b 2
12 c 1
12 d 2
11 e 1
12 f 3
;WITH rownum(code, descd, slnum) AS (
SELECT 10, 'a', 0
UNION SELECT 10, 'b', 0
UNION SELECT 12, 'c', 0
UNION SELECT 12, 'd', 0
UNION SELECT 11, 'e', 0
UNION SELECT 12, 'f', 0
) SELECT code, descd, COUNT(*) OVER (PARTITION BY code ORDER BY code
) FROM rownum ORDER BY descd

How to create a Matrix table on SQL from another table

I have the following table with the following columns.
-------------------------------------
Column1 | Column2 | TotalCount
--------------------------------------
1 1 40
1 2 50
2 1 10
2 2 60
Also, I have another table that is related to Column1 where 1 is X and 2 is Y and same for Column2 where 1 is A and 2 is B
I am trying to come up with a SQL statement that basically gives me some view like...
--------------------
ColumnName | A | B
--------------------
X 40 50
Y 10 60
Any idea how can I achieve that? I've been trying to pivot the data with no luck.
Thanks!
Full working example:
DECLARE #DataSource TABLE
(
[Column1] TINYINT
,[Column2] TINYINT
,[TotalCount] TINYINT
);
INSERT INTO #DataSource ([Column1], [Column2], [TotalCount])
VALUES (1, 1, 40)
,(1, 2, 50)
,(2, 1, 10)
,(2, 2, 60);
SELECT *
FROM
(
SELECT IIF([Column1] = 1, 'X', 'Y') AS [ColumnName]
,IIF([Column2] = 1, 'A', 'B') AS [Column2]
,[TotalCount]
FROM #DataSource
) DS
PIVOT
(
MAX([TotalCount]) FOR [Column2] IN ([A], [B])
) PVT;
Using conditional aggregation:
WITH tbl AS(
SELECT * FROM ( VALUES
(1, 1, 40), (1, 2, 50),
(2, 1, 10), (2, 2, 60)
)t(Column1, Column2, TotalCount)
)
SELECT
ColumnName = CASE WHEN Column1 = 1 THEN 'X' ELSE 'Y' END,
A = MAX(CASE WHEN Column2 = 1 THEN TotalCount END),
B = MAX(CASE WHEN Column2 = 2 THEN TotalCount END)
FROM tbl
GROUP BY Column1
RESULT:
ColumnName A B
---------- ----------- -----------
X 40 50
Y 10 60

How to find the parent ID in a return table

I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc

Resources