SQL server Stuff on multiple columns - sql

I want to concatenate the value of multiple columns for the same ID.
I managed to concatenate for the first column, but when trying the same syntax for the second I'm having an error "The multi-part identifier EnqAct.[ActionID] could not be bound."
Example_Data_Result
Here I managed to group several MachineName together, as seen in "Column1" but I can't manage to group both the MachineName and Description in their own column
My working Query :
SELECT Enq.[EnquiryID],
Enq.[CustomerName],
DetPrio.[Description],
Stuff((SELECT ', ' + Mach.[MachineName]
FROM [dbo].[Machine] Mach
INNER JOIN [dbo].[MachineEnquiry] MachEnq
ON Mach.[MachineID] = MachEnq.[MachineID]
WHERE Enq.[EnquiryID] = MachEnq.[EnquiryID]
FOR XML PATH('')), 1, 2, ''),
DetAct.[Description]
FROM [dbo].[Enquiry] Enq
INNER JOIN [dbo].[EnquiryAction] EnqAct
ON EnqAct.[EnquiryID] = Enq.[EnquiryID]
INNER JOIN [dbo].[DetailsAction] DetAct
ON DetAct.[ActionID] = EnqAct.[ActionID]
INNER JOIN [dbo].[DetailsPriority] DetPrio
ON DetPrio.[PriorityID] = Enq.[Priority]
GROUP BY Enq.[EnquiryID],
Enq.[CustomerName],
DetPrio.[Description],
DetAct.[Description]
My non working Query :
SELECT Enq.[EnquiryID],
Enq.[CustomerName],
DetPrio.[Description],
Stuff((SELECT ', ' + Mach.[MachineName]
FROM [dbo].[Machine] Mach
INNER JOIN [dbo].[MachineEnquiry] MachEnq
ON Mach.[MachineID] = MachEnq.[MachineID]
WHERE Enq.[EnquiryID] = MachEnq.[EnquiryID]
FOR XML PATH('')), 1, 2, ''),
Stuff((SELECT ', ' + DetAct.[Description]
FROM [dbo].[DetailsAction] DetAct
INNER JOIN [dbo].[EnquiryAction] EnqAct
ON EnqAct.[ActionID] = DetAtc.[ActionID]
WHERE Enq.[EnquiryID] = EnqAct.[EnquiryID]
FOR XML PATH('')), 1, 2, '')
FROM [dbo].[Enquiry] Enq
INNER JOIN [dbo].[DetailsPriority] DetPrio
ON DetPrio.[PriorityID] = Enq.[Priority]
GROUP BY Enq.[EnquiryID],
Enq.[CustomerName],
DetPrio.[Description]
Both work the same way, I have a table Enquiry which will have an EnquiryID.
then in my table EnquiryAction or MachineEnquiry I will have entitys with an EnquiryID and an Action/Machine ID
Then in my DetailsAction/Machine Table I will have the Action/Machine ID, and the string I want to get and concatenate.
Why am I having an error and is it possible to achieve what I'm trying to do ?

Related

How do I use a CASE statement in a JOIN in SQL Server?

I have a stored procedure that I want to select a column if the applicationTypeId is in a list of IDs. I am trying to use the CASE statement but I don't know what I am doing wrong. I created a table and added the ApplicationTypeIds to it. The table name is #RelationshipsTypeApplicationId
In other words, what I am trying to do is the following:
If the applicationTypeId is in the #RelationshipsTypeApplicationId table
Join and select the Name column from the IGCRelationshipTypes table
Here is my original stored procedure
WITH AllCustomers AS
(
SELECT
app.ApplicationId,
cust.Name,
ApplicationTypes.TypeName AS ApplicationType,
ApplicationSources.ApplicationSourceName AS ApplicationSource,
CreatedDate = app.CreatedDate,
LastUpdateDate = app.StatusChangeDate,
app.StatusId,
InProgress = STUFF((SELECT ';'+#aw.ApplicationWorkflowName
FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
ORDER BY StepNumber
FOR XML PATH('')), 1, 1, ''),
ActionRequired = (SELECT top 1 1 FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
AND #aw.WorkflowStatusId = 6),
AccountId = STUFF((SELECT ';' + acct.AccountId
FROM #accounts AS acct
WHERE acct.ApplicationId = app.ApplicationId
ORDER BY acct.ParentAccountId
FOR XML PATH('')), 1, 1, '')
FROM
[dbo].[Applications] app
INNER JOIN
[dbo].[Customers] AS cust ON cust.CustomerId = app.CustomerId
INNER JOIN
[dbo].[ApplicationTypes] ON ApplicationTypes.ApplicationTypeId = app.ApplicationTypeId
INNER JOIN
[dbo].[ApplicationSources] ON ApplicationSources.ApplicationSourceId = app.ApplicationSourceId
WHERE
app.StatusId <> '4020-8901-64FFE33F06B1'
AND (#applicationTypeId IS NULL OR app.ApplicationTypeId = #applicationTypeId)
And here is what I updated:
WITH AllCustomers AS
(
SELECT
app.ApplicationId,
cust.Name,
ApplicationTypes.TypeName AS ApplicationType,
ApplicationSources.ApplicationSourceName AS ApplicationSource,
CreatedDate = app.CreatedDate,
LastUpdateDate = app.StatusChangeDate,
app.StatusId,
InProgress = STUFF((SELECT ';' + #aw.ApplicationWorkflowName
FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
ORDER BY StepNumber
FOR XML PATH('')), 1, 1, ''),
ActionRequired = (SELECT top 1 1 FROM #aw
WHERE #aw.ApplicationId = app.ApplicationId
AND #aw.WorkflowStatusId = 6),
AccountId = STUFF((SELECT ';' + acct.AccountId
FROM #accounts as acct
WHERE acct.ApplicationId = app.ApplicationId
ORDER BY acct.ParentAccountId
FOR XML PATH('')), 1, 1, ''),
CASE
WHEN app.ApplicationTypeId IN (SELECT * FROM #RelationshipsTypeApplicationId)
THEN relationshipType.Name
END
FROM
[dbo].[Applications] app
INNER JOIN
[dbo].[Customers] AS cust ON cust.CustomerId = app.CustomerId
INNER JOIN
[dbo].[ApplicationTypes] ON ApplicationTypes.ApplicationTypeId = app.ApplicationTypeId
INNER JOIN
[dbo].[ApplicationSources] ON ApplicationSources.ApplicationSourceId = app.ApplicationSourceId
INNER JOIN
[dbo].[IGCRelationshipTypes] AS relationshipType
ON CASE
WHEN app.ApplicationTypeId IN (SELECT * FROM #RelationshipsTypeApplicationId)
THEN (relationshipType.Name = #relationshipType)
END
WHERE
app.StatusId <> '4020-8901-64FFE33F06B1'
AND (#applicationTypeId IS NULL OR app.ApplicationTypeId = #applicationTypeId)
Here is a screenshot of the syntax error:
Can someone shed some light on what I am missing here?
CASE in T-SQL is an expression that returns a single scalar value, it is not for control of flow. See Dirty secrets of the CASE expression. You'll need something like this, but I don't have the energy to try to determine why you have a table named #RelationshipsTypeApplicationId with a single column (because you really shouldn't say IN (SELECT *)) or if you want to do something different when the match does not exist.
...
ON relationshipType.Name = CASE
WHEN app.ApplicationTypeId IN
(SELECT * FROM #RelationshipsTypeApplicationId)
THEN #relationshipType END
...

SQL get to select multiple names from select subquery

I am writing select statement in SQL Server. I have to add a select query.
select
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID) as Reviewers
from tblUserAccount acct
I need to get more names from a table called tblReviwers. So 1 user from the tblUserAccount table could be associated with multiple reviews.
The tblReviewers only has 3 column AnalystID, ReviewerID, and Date. When I select on the JOIN between TblUserAccount and TblReviewers on a test AccountID = AnalystID, I can get multiple ReviewerIDs, which their firstname and lastname are located in the tblUserAccount. This is the reason why I use a select subquery
When I run the query, I get the following error
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
I am trying to write a VIEW to get a data.
Any help is greatly appreciated.
A query for a column name can only contain a single record result set. If you have an entry with multiple results causes that error.
I think you are missing disclosure of a possible extra component needed in the reviewers table, the who entered it. This will result in a query similar to the following.
The aliases and relations will appear obvious, but need to be confirmed for your actual structure.
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
ReviewBy.FirstNm + ' ' + ReviewBy.LastNm as ReviewerName
from
tblReviewers r
tblUserAccount EnterBy
on r.AccountID = EnterBy.AccountID
tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
REVISION BASED ON ADDITIONAL DATA
Based on providing the analystID on who entered, you should be good with
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
STUFF(( Select
', [' + ReviewBy.LastNm + ', ' + ReviewBy.FirstNm + '] ' AS [text()]
From
tblReviewers r
JOIN tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
where
r.AnaylstID = EnterBy.AccountID
For XML PATH('')), 1, 2, '' ) as AllReviewers
from
tblUserAccount EnterBy
You cannot have a column that contains multiple rows in a single result set, it just doesn't make sense from SQL perspective. Hence the error.
You should JOIN the tblReviewers table instead of subselecting it. That will yield all Reviewers. Then, JOIN again on the tblUserAccount table to get the name of the Reviewer, and you are all set.
SELECT
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
acct2.LastNm + ', ' + acct2.FirstNm as ReviewerName ,
FROM tblUserAccount acct
JOIN tblReviewers revi
ON on acct.AccountID = revi.ReviewerID
JOIN tblUserAccount acct2
ON on acct2.AccountID = revi.AnalystID
Starting from Sql Server 2017, You can use something like STRING_AGG function, which combines values from multiple rows into a single string.
Then, your Reviewers column in your query might look like this:
(select STRING_AGG(Name, ',') from
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID
where ra2.AnalystID = acct.AccountID)
) as Reviewers
In this case, names(first name + last name) of the reviewers for the current user will be separated by commas.
Subselects in select lists can only be used to return one value.
In your case, use joins
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm as Name ,
a.Lastlogin,
a.email as Email,
b.LastNm + ', ' + b.FirstNm as ReviewerName
FROM
tblUserAccount a
LEFT JOIN tblReviewers r ON a.AccountID = r.AnalystID
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
I also used a LEFT JOIN to the reviewers table, in case there is no reviewer defined for an analyst.
You will get several rows per analyst, if more than one reviewer is assigned to it. Usually I solve this problem in the front-end by making a report that puts the main data in a group header (the analyst) and the related date (the reviewers) in the detail section of the report.
Starting with SQL Server 2017, you can also use the STRING_AGG function to concatenate the values of string expressions.
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm AS Name ,
a.Lastlogin,
a.email AS Email,
STRING_AGG(
( SELECT b.LastNm + ' ' + b.FirstNm
FROM
tblReviewers
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
WHERE
r.AnalystID = a.AccountID ),
', ') AS ReviewerName
FROM
tblUserAccount a
This allows you to return more than one reviewer in one field.

What Is Wrong With This FOR XML PATH

I can't seem to figure out why this won't work in MS SQL Server. It seems to not group the lines. Specifically I see:
1036 SC
1036 S1
1094 VO
1094 V1
1094 V2
When I expect to see:
1036 SC,S1
1094 VO,V1,V2
Can someone see something wrong with the syntax?
SELECT DISTINCT oa.acct_cd AS [Account],
STUFF((SELECT ',' + CASE WHEN o.trans_type like 'BUY%' then 'buy of ' else 'sell of ' end + s.ticker AS [text()]
FROM [dbo].[synCRtblTS_ORDER] o INNER JOIN [dbo].[synCRtblCSM_SECURITY] s
ON o.SEC_ID = s.SEC_ID
WHERE o.ORDER_ID = oa.ORDER_ID AND o.status IN ('OPEN','WORK','PENDING')
FOR XML PATH('')), 1, 1, '') [buy/sell]
FROM [dbo].[synCRtblTS_ORDER_ALLOC] oa INNER JOIN tblPortfolio p
ON oa.ACCT_CD = p.Account INNER JOIN tblInvestmentObjective io
ON io.Code = p.InvestmentObjective
WHERE p.AsOfDate = (SELECT AsOfDate FROM tblDateAsOf) and io.CashMgmtStrategy IN ('SC','VO')
GROUP BY oa.ORDER_ID, oa.acct_cd
order by 1
The Information you give is not enough... Within your STUFF you create (and concatenate) string which should contain "buy of" or "sell of". You are missing this information in your output.
It's guessing that your SC, S1, VO values are CashMgmtStrategy entries. You are not concatenating them...
Without deeper knowledge of your tables I cannot solve your problem, but here you'll find a working example of concatenation via FOR XML and STUFF
This code will list all table's names and their columns in a comma delimited list.
SELECT DISTINCT tbls.name AS TableName,
STUFF(
(
SELECT ', ' + cols.name
FROM sys.columns AS cols
WHERE cols.object_id = tbls.object_id
FOR XML PATH('')
), 1, 2, '') AS ColumnList
FROM sys.tables AS tbls

SQL Concatenation using XML Path - multiple rows with multiple table references

I am trying to use the XML Path to run SQL Concatenation but I am running into a bit of a problem. I have one table that is being used as a reference table for values I want to concatenate.I have 3 columns in the reference table (M.PROD, S.PROD, & REF NUMB).
M.PROD=====>S.PROD======>Ref Numb
1===========>_===========>981024583
2===========>_===========>981024719
3===========>A===========>981024605
3===========>B===========>981024669
4===========>A===========>981024688
4===========>B===========>981024706
4===========>C===========>981024723
5===========>_===========>981024742
6===========>_===========>981024742
I have the main tables where the m.prod and s.prod are used to match the reference table for Ref Numb values. What I want to do is concatenate the Ref Numb values based on what is being selected in main tables. The out put I am looking for is this:
M.Prod======>Ref Numb
1===========>981024583
2===========>981024719
3===========>981024605, 981024669
4===========>981024688, 981024706, 981024723
5===========>981024742
6===========>981024742
I am using the following query:
SELECT DISTINCT P.PRODUCT,
(STUFF((SELECT DISTINCT ',' + P1.REFNUMB AS [text()]
FROM PRODUCT P1
WHERE P1.PRODUCT = P.PRODUCT
FOR XML PATH('')), 1, 1, ''))
FROM PRODUCT P
This gives me the output of:
M.Prod======>Ref Numb
1===========>981024583
2===========>981024719
3===========>981024605, 981024669
4===========>981024688, 981024706, 981024723
5===========>981024742
6===========>981024742
However, there are times where all the s.prod are not in the main tables. So for this I use this query:
SELECT DISTINCT P.PRODUCT,
(STUFF((SELECT DISTINCT ',' + P1.REFNUMB AS [text()]
FROM PRODUCT P1
WHERE P1.PRODUCT = P.PRODUCT AND P1.SUBID = P.SUBID
FOR XML PATH('')), 1, 1, ''))
FROM PRODUCT P
This query produces following output for me:
M.Prod======>Ref Numb
1===========>NULL
2===========>NULL
3===========>981024605
3===========>981024669
4===========>981024688
4===========>981024723
5===========>NULL
6===========>NULL
The output I need in these cases is:
M.Prod======>Ref Numb
1===========>981024583
2===========>981024719
3===========>981024605, 981024669
4===========>981024688, 981024723
5===========>981024742
6===========>981024742
Any solution for this will be greatly appreciated, Thank you.
I'm not sure if I understood your question correctly, but try this:
SELECT DISTINCT P.PRODUCT,
(STUFF((SELECT DISTINCT ',' + P1.REFNUMB AS [text()]
FROM PRODUCT P1
WHERE P1.PRODUCT = P.PRODUCT
AND (P1.SUBID = P.SUBID OR P1.SUBID IS NULL AND P.SUBID IS NULL)
FOR XML PATH('')), 1, 1, ''))
FROM PRODUCT P

Parsing error when creating view

I have a view which takes multiple rows and Comma Separated Values (CSV) from table columns with data.
It was working well and I got the results that I needed. Now the view has some parsing error like:
Error in WHERE clause near '('. Unable to parse query text.
Can someone help? Here is my code:
SELECT dbo.table1.title,
Stuff((SELECT ', ' + CONVERT(NVARCHAR(4000), dbo.table3.uid) AS [text()]
FROM dbo.table2
INNER JOIN dbo.table3
ON dbo.table3.uid = dbo.table2.FK_Group
WHERE dbo.table3.uid = dbo.table2.FK_Group
AND dbo.table3.company = dbo.main.company
AND dbo.table2.FK_Version = dbo.table1.fk_Version
AND dbo.main_version.uid = dbo.table1.fk_Version
FOR XML PATH('')), 1, 1, '') AS groupName
FROM dbo.main_version
INNER JOIN dbo.[main]
ON dbo.version.fk_main = dbo.[main].uid
INNER JOIN dbo.main_schema
ON dbo.[main].fk_SCHEMA = dbo.main_schema.uid
INNER JOIN dbo.table1
ON dbo.version.uid = dbo.table1.fk_Version
WHERE ( dbo.main_version.active = 1 )