How to handle String lists in SQL Server [closed] - sql-server

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.

Related

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

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, ''), '')

Select all records when multiple value parameter is blank

I'm creating a report in SSRS and want to have a multiple value parameter but allow blank values (''), and display all records when blank.
The gist of it is:
SELECT *
FROM Products p
JOIN ProductCategories c on c.ProductId = p.Id
WHERE (c.Name IN (#Categories) OR #Categories = '')
Which works when blank, and works with 1 category, but errors out with 2 categories. We got around this by using a temp table, but that solution seemed sort of hacky, so I wanted to see if there was a better way to resolve this.
The temp table workaround we built was this:
CREATE TABLE #temp (ProductId INT, Category NVARCHAR(MAX))
INSERT INTO #temp
SELECT p.Id, c.Name
FROM Products p
JOIN ProductCategories c on c.ProductId = p.Id
WHERE c.Name IN (#Categories)
IF ((SELECT COUNT(*) FROM #temp) = 0)
BEGIN
INSERT INTO #temp
SELECT p.Id, c.Name
FROM Products p
JOIN ProductCategories c on c.ProductId = p.Id
WHERE c.Name LIKE '%'
END
SELECT * FROM #temp
Thanks in advance!
If you don't have a split/parse function
Example
...
Where #Categories = ''
or
C.Name in (
Select RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#Categories,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
)
Here is a dbFiddle ... You'll notice Poultry was excluded, then try it when #Categories=''

How can I eliminate the duplicate values in my answer

I work with the AventureWorks2014 database in Microsoft SQL Server. I need to create a view that will show the CustomerID, the full name and the TOTAL amount sold to client through the web.
My problem is that I can't seem to get the values corresponding to a single customer add up so that a single customer answers to a single line in my result. This is the code I have, any help would be appreciated. I basically need to show the total amount sold to clients on the web.
if object_id('vTotalWebSalesPerCustomer', 'v') is not null
drop view vTotalWebSalesPerCustomer;
go
create view vTotalWebSalesPerCustomer
as
select distinct
c.CustomerID,
ltrim(rtrim(concat(concat(concat(concat(concat(concat(concat(p.Title, ' '), p.LastName), ', '), ' '), p.FirstName), ' '), p.Suffix))) as NomClient,
soh.TotalDue
from
[Sales].[Customer] as c
left join
[Person].[Person] as p on c.CustomerID = p.BusinessEntityID
left join
[Sales].[SalesOrderHeader] as soh on soh.CustomerID = c.CustomerID
where
year(soh.OrderDate) = 2014
and datepart(quarter, soh.OrderDate) = 1
and [OnlineOrderFlag] = 1
go
select *
from vTotalWebSalesPerCustomer
Thanks
Sounds like you need to use GROUP BY and SUM(), example:
SELECT column-names
FROM table-name
WHERE condition
GROUP BY column-names
ORDER BY column-names
Such as:
SELECT SUM(O.TotalPrice), C.FirstName, C.LastName
FROM [Order] O JOIN Customer C
ON O.CustomerId = C.Id
GROUP BY C.FirstName, C.LastName
ORDER BY SUM(O.TotalPrice) DESC
Would return:
Sum FirstName LastName
117483.39 Horst Kloss
115673.39 Jose Pavarotti
113236.68 Roland Mendel
57317.39 Patricia McKenna
52245.90 Paula Wilson
34101.15 Mario Pontes
32555.55 Maria Larsson
DISTINCT filters result rows to remove duplicates. What I think you want is to aggregate rows. Perhaps this will do what you want:
create view vTotalWebSalesPerCustomer as
select c.CustomerID,
ltrim(rtrim(concat(concat(concat(concat(concat(concat(concat(p.Title, ' '), p.LastName), ', '), ' '), p.FirstName), ' '), p.Suffix))) as NomClient,
sum(soh.TotalDue) as TotalDue
from [Sales].[Customer] as c
left join [Person].[Person] as p on c.CustomerID = p.BusinessEntityID
left join [Sales].[SalesOrderHeader] as soh on soh.CustomerID = c.CustomerID
where year(soh.OrderDate) = 2014 and datepart(quarter, soh.OrderDate)=1 and [OnlineOrderFlag] = 1
group by c.CustomerID,NomClient
Note that I removed distinct, added sum operator on the amount you want to total, and group by the customer id and name fields (SQL Server requires that you list in the GROUP BY all result columns which are not being aggregated).
You can use the following VIEW using a GROUP BY on the SELECT:
IF OBJECT_ID('vTotalWebSalesPerCustomer', 'v') IS NOT NULL
DROP VIEW vTotalWebSalesPerCustomer;
GO
CREATE VIEW vTotalWebSalesPerCustomer AS
SELECT
x.CustomerID,
LTRIM(RTRIM(CONCAT(p.Title, ' ', p.LastName, ', ', p.FirstName, ' ', p.Suffix))) AS NomClient,
x.TotalDue
FROM (
SELECT
c.CustomerID AS CustomerID,
SUM(soh.TotalDue) AS TotalDue
FROM [Sales].[Customer] AS c
LEFT JOIN [Sales].[SalesOrderHeader] AS soh ON soh.CustomerID = c.CustomerID
WHERE YEAR(soh.OrderDate) = 2014 AND DATEPART(quarter, soh.OrderDate) = 1 AND [OnlineOrderFlag] = 1
GROUP BY c.CustomerID
)x LEFT JOIN [Person].[Person] AS p ON x.CustomerID = p.BusinessEntityID
GO
Note: You only need one CONCAT function to concat all string values.

Translating complex oracle query to sql server

I'm trying to translate a complex Oracle SQL query in to SQL Server SQL. Here is the query I have to translate:
SELECT TABLE_A.COL_ID, TABLE_A.COL2_ID, TABLE_A.COL3_ID, COL4, COL5, COL6, alias.COL7, alias.COL8
FROM TABLE_A
Inner Join
(SELECT UNIQUE TABLE_A.COL_ID, LISTAGG(TABLE_B.COL4_ID, ',')
WITHIN GROUP (ORDER BY TABLE_A.COL_ID) COL7, LISTAGG(TABLE_C.COL5_ID, ',')
WITHIN GROUP (ORDER BY TABLE_A.COL_ID) COL8
FROM TABLE_A Left Join TABLE_B
on TABLE_A.COL_ID = TABLE_B.COL_ID
Left Join TABLE_C on TABLE_A.COL_ID = TABLE_C.COL_ID
GROUP BY TABLE_A.COL_ID
HAVING (LISTAGG(TABLE_B.COL4_ID)
WITHIN GROUP (ORDER BY TABLE_A.COL_ID)) is not null OR (LISTAGG(TABLE_C.COL5_ID)
WITHIN GROUP (ORDER BY TABLE_A.COL_ID)) is not null ) alias on TABLE_A.COL_ID = alias.COL_ID
Left Join TABLE_D on TABLE_D.COL2_ID = TABLE_A.COL2_ID
Left Join TABLE_E on TABLE_E.COL3_ID = TABLE_A.COL3_ID
Left Join (SELECT DISTINCT COL_ID As idCol FROM TABLE_F
WHERE (TABLE_F.COL6_ID = 363) ) aliasCol on TABLE_E.COL_ID = aliasCol.idCol
ORDER BY COL_ID ASC;
And here is what I tried so far:
use MY_DB;
WITH auxTable AS
(SELECT dbo.TABLE_A.COL_ID, dbo.TABLE_B.COL4_ID, dbo.TABLE_C.COL5_ID FROM dbo.TABLE_A
Left Join dbo.TABLE_B on dbo.TABLE_A.COL_ID = dbo.TABLE_B.COL_ID
Left Join dbo.TABLE_C on dbo.TABLE_A.COL_ID = dbo.TABLE_C.COL_ID )
SELECT dbo.TABLE_A.COL_ID, dbo.TABLE_A.COL2_ID, dbo.TABLE_A.COL3_ID, COL4, COL5, COL6, alias.COL7, alias.COL8
FROM dbo.TABLE_A
Inner Join
(SELECT DISTINCT COL_ID, STUFF(( SELECT ','+ CAST(COL4_ID AS VARCHAR(MAX)) FROM auxTable a WHERE b.COL_ID = a.COL_ID FOR XML PATH('')),1 ,1, '') COL7,
STUFF(( SELECT ','+ CAST(COL5_ID AS VARCHAR(MAX)) FROM auxTable a WHERE b.COL_ID = a.COL_ID FOR XML PATH('')),1 ,1, '') COL8
FROM auxTable b
GROUP BY COL_ID HAVING (STUFF(( SELECT CAST(COL4_ID AS VARCHAR(MAX)) FROM auxTable a WHERE b.COL_ID = a.COL_ID FOR XML PATH('')),1 ,1, '')) is not null
OR (STUFF(( SELECT CAST(COL4_ID AS VARCHAR(MAX)) FROM auxTable a WHERE b.COL_ID = a.COL_ID FOR XML PATH('')),1 ,1, '')) is not null ) alias
on dbo.TABLE_A.COL_ID = alias.COL_ID Left Join dbo.TABLE_D on dbo.TABLE_D.COL2_ID = dbo.TABLE_A.COL2_ID
Left Join dbo.TABLE_E on dbo.TABLE_E.COL3_ID = dbo.TABLE_A.COL3_ID Left Join (SELECT DISTINCT COL_ID As idCol FROM dbo.TABLE_F
WHERE (dbo.TABLE_F.COL6_ID = 363) ) aliasCol on dbo.TABLE_E.COL_ID = aliasCol.idCol ORDER BY COL4 ASC;
I get almost the same results with the two queries but I get 16 rows in Oracle and only 12 rows in SQL Server, I can't understand why.

how to add subjects in same days column not multiple

I have the records below. I want to show subjects when Monday in column all subjects appear on Monday with start time and end time. How do I do this?
SELECT t.teacher_name, tci.class_name, tsb.Subject_Name, tdn.DaysName,
tss.subject_start, tss.subject_end
FROM tblsubjectSchedule tss
INNER JOIN tblsubjects tsb ON tss.subject_id = tsb.Idx
INNER JOIN tblclassinfo tci ON tss.class_id = tci.Idx
INNER JOIN tbldaysnames tdn ON tss.days_id = tdn.Idx
INNER JOIN tblteacher t ON tss.techer_id = t.Idx
WHERE tss.class_id = 2 AND t.school_id = 1
Your attachment is not completely clear. Are you going to ignore teacher_name on result?
In your screenshot you haven't included 'samad teacheer' on Monday results. You want the query only for Monday date?
If you only focus on class and days and date, I got below. please refer the attachment.Let me know if it works for you and accept my answer.
SELECT t1.class_name,t1.daysname,
subject_name =REPLACE( (SELECT (subject_name+'-start:'+CONVERT(VARCHAR, subject_start, 120)+'-End:'+CONVERT(VARCHAR, subject_end, 120)) AS [data()]
FROM [practice].[dbo].[test] t2
WHERE t2.daysname = t1.daysname
ORDER BY subject_name
FOR XML PATH('')
), ' ', ';')
FROM [practice].[dbo].[test] t1
GROUP BY daysname,class_name ;
You could also add teacher name to the schedule column: see the attachment
SELECT t1.class_name,t1.daysname,
schedule =REPLACE( (SELECT (teacher_name+'-'+subject_name+'-start:'+CONVERT(VARCHAR, subject_start, 120)+'-End:'+CONVERT(VARCHAR, subject_end, 120)) AS [data()]
FROM [practice].[dbo].[test] t2
WHERE t2.daysname = t1.daysname
ORDER BY subject_name
FOR XML PATH('')
), ' ', ';')
FROM [practice].[dbo].[test] t1
GROUP BY daysname,class_name ;
Here is the query only for the 'Monday'
SELECT t1.class_name,t1.daysname,
schedule =REPLACE( (SELECT (teacher_name+'-'+subject_name+'-start:'+CONVERT(VARCHAR, subject_start, 120)+'-End:'+CONVERT(VARCHAR, subject_end, 120)) AS [data()]
FROM [practice].[dbo].[test] t2
WHERE t2.daysname = t1.daysname
ORDER BY subject_name
FOR XML PATH('')
), ' ', ';')
FROM [practice].[dbo].[test] t1 where daysname='Monday'
GROUP BY daysname,class_name ;

Resources