Select all users from subgroups in T-SQL query [duplicate] - sql-server

This question already has an answer here:
SQL Server recursive self join
(1 answer)
Closed 3 years ago.
I have a SQL Server table that holds groups and subgroups that I am using to define user permissions in application. What I am trying to accomplish is to select all users ids from all subgroups based on used id assigned to group.
I have tried with this query but it only shows one level of subgroup users.
SET #UserIds =
ISNULL(
STUFF((SELECT ',' + CAST(tbl.UserID AS VARCHAR(MAX))
FROM
(SELECT grpUser.UserId AS UserID
FROM dbo.GroupUser grpUser
INNER JOIN (SELECT subGrp.Id, subGrp.GroupName
FROM dbo.Groups grp
INNER JOIN dbo.GroupUser guser ON grp.Id = guser.GroupId
AND guser.UserId = #UserId
INNER JOIN dbo.Groups subGrp ON grp.Id = subGrp.ParentGroupId) tbl ON grpUser.GroupId = tbl.Id) AS tbl
FOR XML PATH('')), 1, 1, ''), '')
I am struggling to get users ids in all subgroups. Number of subgroups is not defined. Is there a way to do in sql or should I shift this part to application side? Thank you for any advice.

Finally I came up with this code. Thank you for hint.
declare #UserGroupId int;
Set #UserGroupId = (Select GroupId from GroupUser gu
left join dbo.Groups gr on gu.GroupId = gr.id
where UserId = #UserId and gr.IsDeleted = 0)
declare #UsersGroups Table (
Id int not null,
GroupName nvarchar(50) not null
)
;WITH grp AS
(
SELECT Id, GroupName
FROM Groups
WHERE ParentGroupId = 1
UNION ALL
SELECT g.Id, g.GroupName
FROM Groups g
INNER JOIN dbo.GroupUser guser ON g.Id = guser.GroupId
INNER JOIN grp ON g.ParentGroupId = grp.Id
)
Insert into #UsersGroups
SELECT Id, GroupName
FROM grp
SET #UserIds = ISNULL(STUFF(( SELECT ',' + CAST(tbl.UserID AS VARCHAR(max)) FROM (
SELECT grpUser.UserId AS UserID FROM dbo.GroupUser grpUser
INNER JOIN ( SELECT * FROM #UsersGroups
) tbl ON grpUser.GroupId = tbl.Id
) AS tbl FOR XML PATH('') ), 1, 1, ''), '')

Related

How to handle String lists in SQL Server [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have two tables as follow:
Let's say tbl1(Which keeps the main applicant info) and tbl2(the main applicant with family members info, note that we must keep the main applicant here too)
I'm trying to check whether the main applicant added oneself to tbl2 or not (that means if main applicant info is in tbl2)
CID FullName
-----------------------
1001 AYNALEM A NIGUSSIE
while in tbl2 (Same CID as tbl1):
HID CID FullName
-------------------------------------
1 1001 AYNALEM A NIGUSSIE
2 1001 CARLSTON HEITH
3 1001 Q LEE
I declared a variable to hold FullName list from tbl2 and compare the full name from tbl1 (to check if it exists in tbl2, function [dbo].[ufn_GetFullName] is used to get fullName from tbl1 ).
DECLARE #HH_FullName VARCHAR(250);
SELECT
#HH_FullName = (SELECT
STUFF((SELECT DISTINCT ', '+ DR.FullName
FROM
(SELECT DR.HID, DR.FullName
FROM dbo.tbl1 DR WITH(NOLOCK)
WHERE DR.CID = PC.CID) DR
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'), 1, 2, ''))
FROM [dbo].[tbl2] PC WITH (NOLOCK)
LEFT JOIN [dbo].[tbl1] HC WITH (NOLOCK) ON HC.CID = PC.CID
WHERE PC.CID = 100037
/* --SELECT #HH_FullName -- 'AYNALEM A NIGUSSIE', 'CARLSTON
HEITH', 'Q LEE'
SELECT
(STUFF((SELECT ',''' + Val+''''
FROM
(SELECT CONVERT(VARCHAR(250), value) AS Val
FROM string_split(#HH_FullName, ',')) DF
FOR XML PATH('')), 1, 1, '')) -- 'AYNALEM A NIGUSSIE',' CARLSTON HEITH',' Q LEE'
SELECT CONVERT(VARCHAR(250), value) AS Val
FROM string_split(#HH_FullName, ',')
*/
SELECT *
FROM [dbo].[tbl1] PC WITH(NOLOCK)
LEFT JOIN [dbo].[tbl2] HC WITH(NOLOCK) ON HC.ClaimantID = PC.ClaimantID
WHERE HC.CID IS NOT NULL
AND ([dbo].[ufn_GetFullName](Pc.FirstName, Pc.MiddleName, Pc.LastName)
NOT IN ( SELECT ( STUFF((
SELECT ',''' + Val+''''
FROM (SELECT convert(VARCHAR(250), value) AS Val FROM string_split(#HH_FullName, ','))DF
FOR XML PATH('')
), 1, 1, '')) ))
-- OR as follow
SELECT * FROM [dbo].[tbl1] PC WITH(NOLOCK)
LEFT JOIN [dbo].[tbl2] HC WITH(NOLOCK) ON
HC.CID = PC.CID
WHERE HC.CID IS NOT NULL AND
( [dbo].[ufn_GetFullName]
(Pc.FirstName,Pc.MiddleName,Pc.LastName) NOT IN (SELECT convert(VARCHAR(250), value)
as VAL FROM string_split(#HH_FullName, ',') ) )
-- Also tried this way, it won't work either
SELECT * FROM [dbo].[tbl1] PC WITH(NOLOCK)
LEFT JOIN [dbo].[tbl2] HC WITH(NOLOCK) ON
HC.CID = PC.CID
WHERE HC.CID IS NOT NULL AND
( [dbo].[ufn_GetFullName]
(Pc.FirstName,Pc.MiddleName,Pc.LastName) NOT IN ( #HH_FullName )
WHERE Cond NOT IN (#HH_FullName) not returning the right value.
Like #UnhandledExcepSean commented you are complicating this beyond your stated objective...
I want to check if the FullName in tbl1 also exists in tbl2.
This statement will do it...
SELECT t1.*
FROM tbl1 AS t1
INNER JOIN tbl2 AS t2
ON CONCAT_WS(' ', t1.FirstName, t1.MiddleNAme, t1.LastName) = t2.FullName
This would work too...
SELECT t1.*
FROM tbl1 as t1
WHERE EXISTS (
SELECT * FROM tbl2
WHERE FullName = CONCAT_WS(' ', t1.FirstName, t1.MiddleNAme, t1.LastName))
Another way...
SELECT t1.*
FROM tbl1 as t1
WHERE CONCAT_WS(' ', t1.FirstName, t1.MiddleNAme, t1.LastName)
IN (SELECT FullName FROM tbl2)
You will have to account for the case where there is no middle name. Is it NULL or just an empty string? Or perhaps even spaces?
For small volumes of data you are not going to be able to detect a difference in performance. However, for larger amounts of data I believe you will be better off with the EXISTS approach.

Get full hierarchy by children

I have 2 tables:
contains full tree data
contains only specific childs
I need to get full hierarchy by child values. I can do it by one specific child node by following way:
;with tree as
(
select id, parent_id, name, level from f_all where id = #specefic_id
union all
select f.id, f.parent_id, f.name, f.level from f_all f
inner join tree t on f.id = t.parent_id and f.id <> f.parent_id
)
select *
from tree
OPTION (Maxrecursion 0)
I have an idea but I think it is not good. My idea is create function with above code. And call it by select my second table. I even didn't try it. Can you give me a right direction.
This is 2012+ ( Using concat() ... easily converted ).
Declare #f_all table (id int,parent_id int,name varchar(50))
Insert into #f_all values
(1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8')
Declare #Top int = null --<< Sets top of Hier Try 9
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
Declare #Filter varchar(25) = '4,6' --<< Empty for All or try 4,6
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500))
,ID
,parent_id
,Lvl=1
,name
From #f_all
Where IsNull(#Top,-1) = case when #Top is null then isnull(parent_id,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500))
,r.ID
,r.parent_id
,p.Lvl+1
,r.name
From #f_all r
Join cteP p on r.parent_id = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Distinct
A.R1
,B.R2
,A.ID
,A.parent_id
,A.Lvl
,name = Replicate(#Nest,A.Lvl-1) + A.name
From cteR1 A
Join cteR2 B on A.ID=B.ID
Join (Select R1 From cteR1 where IIF(#Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',#Filter+','))>0) F on F.R1 between A.R1 and B.R2
Order By A.R1
Returns (#Top=null and #Filter='4,6')
Return Full Hier (#Top=null and #Filter='')
Returns Just a portion (#Top=2 and #Filter='')
The problem for me was that i didn't know how cte recursion works. Now i know how it works line by line: Recursive Queries Using Common Table Expressions.
Code below returns all hierarchy by children nodes:
;with tree as(
select id, parent_id, name, level from f_all fa
inner join #2nd_table_cildren_id c on c.id = fa.id
union all
select f.id, f.parent_id, f.name, f.level from f_all f
inner join tree t on f.id = t.parent_id and f.id <> f.parent_id
)
select distinct *
from tree
OPTION (Maxrecursion 0)

TSQL Rewrite UNION into JOIN

I'm new to SQL and I'm trying to rewrite this query so that it uses a join instead of a union
DECLARE #user VARCHAR(255) = 'jSmith'
DECLARE #dept VARCHAR(255) = 'produce'
SELECT DISTINCT A.*
FROM goals A
INNER JOIN managers B ON B.name = #user
WHERE #user IN (A.userName,A.managerUserName)
AND dept = #dept
AND yr = '2016'
UNION
SELECT DISTINCT A.*
FROM goals A
INNER JOIN managers C ON C.name = #user
INNER JOIN committedGoals B ON A.goalID = B.goalID
WHERE dept LIKE #dept + '%'
AND yr = '2016'
This is not exactly the same because you've restricted the second part of the query to records that contain a record in committedGoals, but if you always have a record, then this might work for you:
SELECT g.*
FROM goals g
LEFT JOIN managers m
ON g.managerUserName=m.name
WHERE dept LIKE #dept + '%'
AND yr='2016'
AND (g.managerUserName=#user OR m.name=#user)

Multiple COUNT(*) with join

I have to COUNT some rows from multiple tables. Before I can do multiple COUNT I will have to subselect. The problem here is that I need to JOIN some values in order to get the right result.
SELECT
sponsor.Name As SponsorName,
COUNT(participants.[Table]) AS ParticipantCount,
( SELECT
COUNT(guestcards.[Table])
FROM
guestcards
WHERE
guestcards.EventID = #EventID
AND
guestcards.[Table] = #Table
AND
guestcards.SponsorID = participants.SponsorID
-- Here lies the problem.
-- I will need to check up on another value to ensure I get the right rows, but participants.SponsorID is not here because of no join :-(
) AS GuestParticipantCount
FROM
participants
LEFT JOIN
sponsor
ON
sponsor.ID = participants.SponsorID
WHERE
participants.EventID = #EventID
AND
participants.[Table] = #Table
GROUP BY
sponsor.Name
Guestcards table holds: sponsorid, eventid, tablename
Participantstable holds: sponsorid, eventid, tablename
Sponsor table holds: id, name
I need to count how many "Participants" there are and how many "Guestcards" that in a particulary event. These participants have a table (where they should sit) and so does the guestcards. I need to check up on if its the same "table" where they sit.
So I need to count how many are sitting at table "A1" or table "A2" etc.
The result I am after is like:
"Sponsor Name has 5 participants and 3 guestcards. They sit on A1"
I hope I made my self clear
Here's exact equivalent of you query (grouping on sponsor.Name):
SELECT sponsor.name,
COALESCE(SUM(participantCount), 0),
COALESCE(SUM(guestcardsCount), 0)
FROM (
SELECT sponsorId, COUNT(*) AS participantCount
FROM participants
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) p
FULL JOIN
(
SELECT sponsorId, COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) g
ON g.sponsorId = p.sponsorId
FULL JOIN
sponsor s
ON s.id = COALESCE(p.sponsorId, g.sponsorId)
GROUP BY
s.sponsorName
However, I believe you want something more simple:
SELECT sponsorName, participantCount, guestcardsCount
FROM sponsor s
CROSS APLLY
(
SELECT COUNT(*) AS participantCount
FROM participants
WHERE sponsorId = s.id
AND eventId = #eventId
AND [table] = #table
) p
CROSS APLLY
(
SELECT COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE sponsorId = s.id
AND eventId = #eventId
AND [table] = #table
) g
Update:
SELECT sponsor.name,
COALESCE(participantCount, 0),
COALESCE(guestcardsCount, 0)
FROM (
SELECT sponsorId, COUNT(*) AS participantCount
FROM participants
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) p
FULL JOIN
(
SELECT sponsorId, COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) g
ON g.sponsorId = p.sponsorId
JOIN sponsor s
ON s.id = COALESCE(p.sponsorId, g.sponsorId)

Get the most value from another column

I want to get data historical and the production. My stored procedure is as follows:
ALTER PROCEDURE [dbo].[pCaRptACInactivas](
#CodAsesor VARCHAR(15),
#CodOficina VARCHAR(4))
AS
SET NOCOUNT ON
DECLARE #CodArbolConta VARCHAR(25)
IF #CodOficina = '%'
SET #CodArbolConta = '%'
ELSE
SELECT #CodArbolConta = CodArbolConta + '%' FROM tClOficinas WHERE CodOficina LIKE #CodOficina
SELECT
tabACInactivas.CodOficina,
tabACInactivas.NomOficina,
tabACInactivas.NomAsesor,
MAX(tabACInactivas.CodPrestamo) CodPrestamo,
tabACInactivas.CodAsociacion,
tabACInactivas.NombreAsociacion,
MAX(tabACInactivas.Ciclo) AS Ciclo,
COUNT(DISTINCT tabACInactivas.CodUsuario) AS CantSocias,
MAX(tabACInactivas.FechaEstado) AS FechaCancelacion--,
FROM ( SELECT tClOficinas.CodOficina, tClOficinas.NomOficina, tCaClAsesores.CodAsesor, tCaClAsesores.NomAsesor, tCaPrestamos.CodPrestamo, tCaAsociacion.CodAsociacion, tCaAsociacion.NombreAsociacion, tCaPrestamos.Ciclo, tCaPrCliente.CodUsuario, tCaPrestamos.FechaEstado, tClParametros.FechaProceso FROM tCaPrestamos WITH(NOLOCK) INNER JOIN tCaProducto WITH(NOLOCK) ON tCaProducto.CodProducto = tCaPrestamos.CodProducto INNER JOIN tClOficinas WITH(NOLOCK) ON tClOficinas.CodOficina = tCaPrestamos.CodOficina INNER JOIN tCaAsociacion WITH(NOLOCK) ON tCaAsociacion.CodAsociacion = tCaPrestamos.CodAsociacion INNER JOIN tCaPrCliente WITH(NOLOCK) ON tCaPrCliente.CodPrestamo = tCaPrestamos.CodPrestamo INNER JOIN tClParametros WITH(NOLOCK) ON tClParametros.CodOficina = tClOficinas.CodOficina INNER JOIN tCaClAsesores ON tCaClAsesores.CodAsesor = tCaAsociacion.CodAsesor WHERE tCaPrestamos.Estado = 'CANCELADO' AND DATEDIFF(DAY, tCaPrestamos.FechaEstado, tClParametros.FechaProceso) > 30 AND NOT EXISTS(SELECT 1
FROM tCaPrestamos Pr
INNER JOIN tCaPrCliente PrCl ON PrCl.CodPrestamo = Pr.CodPrestamo
WHERE Pr.Estado NOT IN ('TRAMITE', 'APROBADO')
AND Pr.FechaDesembolso >= tCaPrestamos.FechaEstado
AND Pr.CodAsociacion = tCaPrestamos.CodAsociacion
) AND tCaProducto.Tecnologia = 3 AND tCaPrestamos.CodAsesor LIKE #CodAsesor AND tCaPrestamos.CodOficina IN (SELECT CodOficina FROM tClOficinas WHERE CodArbolConta LIKE #CodArbolConta)
UNION ALL
SELECT tClOficinas.CodOficina, tClOficinas.NomOficina, tCaClAsesores.CodAsesor, tCaClAsesores.NomAsesor, tCaHPrestamos.CodPrestamo, tCaAsociacion.CodAsociacion, tCaAsociacion.NombreAsociacion, tCaHPrestamos.Ciclo, tCaHPrCliente.CodUsuario, tCaHPrestamos.FechaEstado, tClParametros.FechaProceso FROM tCaHPrestamos WITH(NOLOCK) INNER JOIN tCaProducto WITH(NOLOCK) ON tCaProducto.CodProducto = tCaHPrestamos.CodProducto INNER JOIN tClOficinas WITH(NOLOCK) ON tClOficinas.CodOficina = tCaHPrestamos.CodOficina INNER JOIN tCaAsociacion WITH(NOLOCK) ON tCaAsociacion.CodAsociacion = tCaHPrestamos.CodAsociacion INNER JOIN tCaHPrCliente WITH(NOLOCK) ON tCaHPrCliente.CodPrestamo = tCaHPrestamos.CodPrestamo INNER JOIN tClParametros WITH(NOLOCK) ON tClParametros.CodOficina = tClOficinas.CodOficina INNER JOIN tCaClAsesores ON tCaClAsesores.CodAsesor = tCaAsociacion.CodAsesor WHERE tCaHPrestamos.Estado = 'CANCELADO' AND DATEDIFF(DAY, tCaHPrestamos.FechaEstado, tClParametros.FechaProceso) > 30 AND NOT EXISTS(SELECT 1
FROM tCaHPrestamos Pr
INNER JOIN tCaHPrCliente PrCl ON PrCl.CodPrestamo = Pr.CodPrestamo
WHERE Pr.Estado NOT IN ('TRAMITE', 'APROBADO')
AND Pr.FechaDesembolso >= tCaHPrestamos.FechaEstado
AND Pr.CodAsociacion = tCaHPrestamos.CodAsociacion
) AND tCaProducto.Tecnologia = 3 AND tCaHPrestamos.CodAsesor LIKE #CodAsesor AND tCaHPrestamos.CodOficina IN (SELECT CodOficina FROM tClOficinas WHERE CodArbolConta LIKE #CodArbolConta)
)tabACInactivas
GROUP BY tabACInactivas.CodAsociacion, tabACInactivas.NombreAsociacion, tabACInactivas.NomOficina, tabACInactivas.CodOficina, tabACInactivas.NomAsesor
I want the CantSocias column takes the most value of the Ciclo column, but not working
That stored procedure that you have posted up is way too large and blocky to even try to interpret and understand. So I will go off of your last sentence:
I want the CantSocias column takes the most value of the Ciclo column,
but not working
Basically if you want to set a specific column to that, you can do something like this:
update YourTable
set CantSocias =
(
select max(Ciclo)
from YourOtherTable
)
-- here is where you can put a conditional WHERE clause
You may need to create a sub query to get the most value of Ciclo and join back to your query.
An example of what I mean is here:
create table #Product
(
ID int,
ProductName varchar(20)
)
insert into #Product(ID, ProductName)
select 1,'ProductOne'
union
select 2,'ProductTwo'
create table #ProductSale
(
ProductID int,
Number int,
SalesRegion varchar(20)
)
insert into #ProductSale(ProductID,Number,SalesRegion)
select 1,1500,'North'
union
select 1, 1200, 'South'
union
select 2,2500,'North'
union
select 2, 3200, 'South'
--select product sales region with the most sales
select * from #Product p
select ProductId, Max(Number) as Bestsale from #ProductSale ps group by ProductID
--combining
select
p.ID,
p.ProductName,
tp.Bestsale,
ps.SalesRegion
from
#Product p
inner join
(select ProductId, Max(Number) as Bestsale from #ProductSale ps group by ProductID) as tp on p.ID = tp.ProductID
inner join
#ProductSale ps on p.ID = ps.ProductID and tp.Bestsale = ps.Number

Resources