How to combine different counts in one SQL Server query - sql

I would like to combine different Count statements in one query so that in the end the query just returns one number (total) for each Count.
The following queries work separately but I could not find a way to combine them without repeating the same result (nested tables) and without losing the separate names for each of them (Union All).
My SQL (just combined for easier view here):
(
SELECT COUNT(*) OVER() AS countB
FROM MOC_Log2 B
WHERE B.modBy = #modBy
AND B.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
),
(
SELECT COALESCE(SUM(D.vote), '0') AS countC
FROM MOC_Log3 C
LEFT JOIN MOC_Log3_Votes D
ON D.itemID = C.itemID
WHERE C.modBy = #modBy
AND C.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
),
(
SELECT COALESCE(SUM(F.vote), '0') AS countE
FROM MOC_Log4 E
LEFT JOIN MOC_Log4_Votes F
ON F.itemID = E.itemID
WHERE E.modBy = #modBy
AND E.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
)

just add null columns?
SELECT COUNT(*) OVER() AS countB,
Null AS countC,
Null AS countE
FROM MOC_Log2 B
WHERE B.modBy = #modBy
AND B.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
Union
SELECT null,
COALESCE(SUM(D.vote), '0'),
null
FROM MOC_Log3 C
LEFT JOIN MOC_Log3_Votes D
ON D.itemID = C.itemID
WHERE C.modBy = #modBy
AND C.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
Union
SELECT null,
null,
COALESCE(SUM(F.vote), '0')
FROM MOC_Log4 E
LEFT JOIN MOC_Log4_Votes F
ON F.itemID = E.itemID
WHERE E.modBy = #modBy
AND E.lastUpdate = 'Added'
FOR XML PATH(''), ELEMENTS, TYPE
Also note I used Union over Union all. It is hard to tell if this would work for you as I cannot tell if the extra null columns may cause some error.

Related

The multi-part identifier could not be bound - stuff cmd

I'm attempting a Stuff Cmd to combine multiple rows to a single entry. I keep getting "The multi-part identifier "SPCLT.CD_VAL_DESC" could not be bound." (under the first SELECT statement)
STUFF(
(SELECT
**',' + SPCLT.CD_VAL_DESC**
FROM
(
SELECT DISTINCT
SPCLT.CD_VAL_DESC SPECIALTY
FROM PIN_STATUS PS
INNER JOIN PROV_TYPE_SPCLT SPC
ON PS.PROV_ID = SPC.PROV_ID
AND SPC.VLDT_IND = 'Y'
INNER JOIN CODE_REF SPCLT
ON SPC.SPCLT_CD = SPCLT.CD_VAL
AND SPCLT.CD_REF_NM = 'SPECIALTY'
AND SPCLT.VLDT_IND = 'Y'
WHERE SPC.VLDT_IND = 'Y'
) SPCLTY
for xml
path('')
)
,1,1,'') SPECIALTIES
You need to pay attention to the format of your SQL, and then the answer would probably jump out and bite you on the nose... happens to everyone.
Your query:
STUFF(
(
SELECT
',' + SPCLT.CD_VAL_DESC
FROM
(
SELECT DISTINCT
SPCLT.CD_VAL_DESC SPECIALTY
FROM PIN_STATUS PS
INNER JOIN PROV_TYPE_SPCLT SPC
ON PS.PROV_ID = SPC.PROV_ID
AND SPC.VLDT_IND = 'Y'
INNER JOIN CODE_REF SPCLT
ON SPC.SPCLT_CD = SPCLT.CD_VAL
AND SPCLT.CD_REF_NM = 'SPECIALTY'
AND SPCLT.VLDT_IND = 'Y'
WHERE SPC.VLDT_IND = 'Y'
) SPCLTY
for xml path('')
)
,1,1,'') SPECIALTIES
...is divided into sub-queries. The STUFF() function is acting on the first SELECT beneath it.
That first SELECT is taking data FROM a sub-query, which has been aliased as SPCLTY. So, naturally, within that SELECT, you need to be referencing SPCLTY and not SPCLT.
Adding a bit of whitespace makes it a little clearer, I think.

display more than one value using a SQL query

I am trying to display multiple authors per title in a single column. At the moment there a repeating rows, due to the fact that some Titles have more than 1 FirstName. Is there a form of concatenation that can be used to resolve this and display all the authors in a single filed and perhaps separated by a comma.
This is my current query:
SELECT
Submission.Title, Researcher.FirstName, Submission.Type
FROM
Submission
INNER JOIN
((Faculty
INNER JOIN
School ON Faculty.FacultyID = School.[FacultyID])
INNER JOIN
(Researcher
INNER JOIN
ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID)
ON School.SchoolID = Researcher.SchoolID)
ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY
Submission.Title, Researcher.FirstName, Submission.Type;
This the output it generates:
[
this is the output I am trying to generate:
Title FirstName Type
---------------------------------------------------------------------------
21st Century Business Matthew, Teshar Book Chapter
A Family Tree... Keshant, Lawrence Book Chapter
Benefits of BPM... Jafta Journal Article
Business Innovation Matthew, Morna, Teshar Book Chapter
You may inclde the concantenation logic within a CROSS APPLY
SELECT
Submission.Title
, CA.FirstNames
, Submission.Type
FROM Submission
CROSS APPLY (
SELECT
STUFF((
SELECT /* DISTINCT ??? */
', ' + r.FirstName
FROM ResearcherSubmission rs
INNER JOIN Researcher r ON r.ResearcherID = rs.ResearcherID
WHERE Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')
) AS CA (FirstNames)
GROUP BY
Submission.Title
, CA.FirstNames
, Submission.Type
;
NB: I'm not sure if you need to include DISTINCT into the subquery when concatenating the names, e.g. if these was 'Jane' (Smith) and 'Jane' (Jones) do you want the final list as: 'Jane' or 'Jane, Jane'?
You can do this in your application logic as well.
But if you want to do this with a query. You should be able do something like this:
SELECT DISTINCT
sm.Title,
STUFF(
(SELECT ', ' + r.FirstName
FROM ResearcherSubmission rs
INNER JOIN Researcher r ON r.ResearcherID = rs.ResearcherID
WHERE sm.SubmissionID = rs.SubmissionID
FOR XML PATH('')), 1, 2, '') AS FirstNames,
sm.Type
FROM Submission sm
You can use the below query to generate the o/p as you want from the o/p that you have got.
CREATE TABLE #temptable(Title VARCHAR(200), FirstName VARCHAR(200), Type VARCHAR(200))
INSERT INTO #temptable
SELECT 'Book1','Matt','Chapter' UNION
SELECT 'Book1','Tesh','Chapter' UNION
SELECT 'BPM','Jafta','Article' UNION
SELECT 'Ethics','William','Journal' UNION
SELECT 'Ethics','Lawrence','Journal' UNION
SELECT 'Ethics','Vincent','Journal' UNION
SELECT 'Cellular','Jane','Conference'
SELECT Title
,STUFF((SELECT ', ' + CAST(FirstName AS VARCHAR(10)) [text()]
FROM #temptable
WHERE Title = t.Title
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
,Type
FROM #temptable t
GROUP BY Title,Type

How to concatenate distinct columns of otherwise duplicate rows into one row without using FOR XML PATH('')?

I have this query:
SELECT DISTINCT
f1.CourseEventKey,
STUFF
(
(
SELECT '; ' + Title
FROM (
SELECT DISTINCT
ces.CourseEventKey,
f.Title
FROM CourseEventSchedule ces
INNER JOIN Facility f ON f.FacilityKey = ces.FacilityKey
WHERE ces.CourseEventKey IN
(
SELECT CourseEventKey
FROM #CourseEvents
)
) f2
WHERE f2.CourseEventKey = f1.CourseEventKey
FOR XML PATH('')
), 1, 2, ''
)
FROM (
SELECT DISTINCT
ces.CourseEventKey,
f.Title
FROM CourseEventSchedule ces
INNER JOIN Facility f ON f.FacilityKey = ces.FacilityKey
WHERE ces.CourseEventKey IN
(
SELECT CourseEventKey
FROM #CourseEvents
)
) f1
It produces this result set:
CourseEventKey Titles
-------------- ----------------------------------
29 Test Facility 1
30 Memphis Training Room
32 Drury Inn & Suites Creve Coeur
The data is accurate, but I can't have FOR XML PATH('') because it escapes certain special characters.
To be clear, I'm using FOR XML PATH('') because it is possible for records with the same CourseEventKey to have multiple Facility titles associated with them.
How can I retain the data returned by this query without using FOR XML PATH('')?
Change the "for xml path('') )" section to "for xml path(''), root('root'), type).query('root').value ('.', 'varchar(max)')" this will unescape the characters correctly.
Sorry for the poor formatting but not at my computer right now. I can give a full example later if you need.

how to convert xml out put of a TSQL query into varchar output

I am trying to generate
SELECT DISTINCT
P.DOMAIN_ID,
P.SOURCE_SYSTEM_ID
FROM EDW.dbo.DOMAIN_VALUE AS P
WHERE P.ID = 4
AND CURRENT_FLAG = 'Y'
EXCEPT
( SELECT F.DOMAIN_ID,
F.SOURCE_SYSTEM_ID
FROM EDW.dbo.DOMAIN AS F
WHERE F.ID = 4
AND F.CURRENT_FLAG = 'Y'
)
FOR XML PATH('DOMAIN'),
ROOT('DOMAIN_VALUE')
The output value in XML in Result tab as
<REFERENCE_DOMAIN_VALUE>
<REFERENCE_DOMAIN>
<REFERENCE_DOMAIN_ID>10799</REFERENCE_DOMAIN_ID>
<REFERENCE_SOURCE_SYSTEM_ID>7452-001</REFERENCE_SOURCE_SYSTEM_ID>
</REFERENCE_DOMAIN>
</REFERENCE_DOMAIN_VALUE>
Now I need to convert this XML out to varchar(max) but the result needs to be same.
Just subquery it into a scalar value and convert it. The trick here is that FOR XML in a subquery and EXCEPT on top don't mix, so subquery the EXCEPT part first.
SELECT CONVERT(varchar(max), (
SELECT * FROM (
SELECT DISTINCT P.DOMAIN_ID, P.SOURCE_SYSTEM_ID
FROM EDW.dbo.DOMAIN_VALUE AS P
WHERE P.ID = 4 AND CURRENT_FLAG = 'Y'
EXCEPT (
SELECT F.DOMAIN_ID, F.SOURCE_SYSTEM_ID
FROM EDW.dbo.DOMAIN AS F
WHERE F.ID = 4 AND F.CURRENT_FLAG = 'Y' )
) I
FOR XML PATH('DOMAIN'), ROOT('DOMAIN_VALUE')
))

How should I modify this SQL statement?

My SQL Server view
SELECT
geo.HyperLinks.CatID, geo.Tags.Tag, geo.HyperLinks.HyperLinksID
FROM
geo.HyperLinks LEFT OUTER JOIN
geo.Tags INNER JOIN
geo.TagsList ON geo.Tags.TagID = geo.TagsList.TagID ON geo.HyperLinks.HyperLinksID = geo.TagsList.HyperLinksID WHERE HyperLinksID = 1
returns these...
HyperLinksID CatID Tags
1 2 Sport
1 2 Tennis
1 2 Golf
How should I modify the above to have results like
HyperLinksID CatID TagsInOneRowSeperatedWithSpaceCharacter
1 2 Sport Tennis Golf
UPDATE: As Brad suggested I came up here...
DECLARE #TagList varchar(100)
SELECT #TagList = COALESCE(#TagList + ', ', '') + CAST(TagID AS nvarchar(100))
FROM TagsList
WHERE HyperLinksID = 1
SELECT #TagList
Now the result looks like
HyperLinksID CatID TagsInOneRowSeperatedWithSpaceCharacter
1 2 ID_OF_Sport ID_OF_Tennis ID_OF_Golf
And of course I have to combine the contents from the#TagList variable and the original SELECT statement...
Which means that I'll have to wait for the holy SO bounty :(
If SQL, try this post:
Concatenating Row Values
If you want to try your hand at CLR code, there are examples of creating a custom aggregate function for concatenation, again, for MS SQL.
This post is pretty exhaustive with lots of ways to accomplish your goal.
Using the approach from here to avoid any issues if your tag names contain special XML characters:.
;With HyperLinks As
(
SELECT 1 AS HyperLinksID, 2 AS CatID
),
TagsList AS
(
SELECT 1 AS TagId, 1 AS HyperLinksID UNION ALL
SELECT 2 AS TagId, 1 AS HyperLinksID UNION ALL
SELECT 3 AS TagId, 1 AS HyperLinksID
)
,
Tags AS
(
SELECT 1 AS TagId, 'Sport' as Tag UNION ALL
SELECT 2 AS TagId, 'Tennis' as Tag UNION ALL
SELECT 3 AS TagId, 'Golf' as Tag
)
SELECT HyperLinksID,
CatID ,
(SELECT mydata
FROM ( SELECT Tag AS [data()]
FROM Tags t
JOIN TagsList tl
ON t.TagId = tl.TagId
WHERE tl.HyperLinksID = h.HyperLinksID
ORDER BY t.TagId
FOR XML PATH(''), TYPE
) AS d ( mydata ) FOR XML RAW,
TYPE
)
.value( '/row[1]/mydata[1]', 'varchar(max)' ) TagsInOneRowSeperatedWithSpaceCharacter
FROM HyperLinks h
Edit: As KM points out in the comments this method actually automatically adds spaces so I've removed the manually added spaces. For delimiters other than spaces such as commas Peter's answer seems more appropriate.
If you know your data will not contain any problematic characters then a simpler (probably more performant) version is
SELECT CatID ,
HyperLinksID,
stuff(
( SELECT ' ' + Tag
FROM Tags t
JOIN TagsList tl
ON t.TagId = tl.TagId
WHERE tl.HyperLinksID = h.HyperLinksID
ORDER BY t.TagId
FOR XML PATH('')
), 1, 1, '') TagsInOneRowSeperatedWithSpaceCharacter
FROM HyperLinks h
Use FOR XML in a correlated subquery. For a space-delimited list:
SELECT h.HyperLinksID, h.CatID
, TagList = (
SELECT t.Tag AS [data()]
FROM geo.TagList l
JOIN geo.Tags t ON l.TagId = t.TagId
WHERE l.HyperLinksID = h.HyperLinksID
ORDER BY t.Tag
FOR XML PATH(''), TYPE
).value('.','NVARCHAR(MAX)')
FROM geo.HyperLinks AS h
WHERE h.HyperLinksID = 1
For any other delimiter:
SELECT h.HyperLinksID, h.CatID
, TagList = STUFF((
SELECT ', '+t.Tag
FROM geo.TagList l
JOIN geo.Tags t ON l.TagId = t.TagId
WHERE l.HyperLinksID = h.HyperLinksID
ORDER BY t.Tag
FOR XML PATH(''), TYPE
).value('.','NVARCHAR(MAX)')
,1,2,'')
FROM geo.HyperLinks AS h
WHERE h.HyperLinksID = 1
The subquery creates a delimited list, and then STUFF(...,1,2,'') removes the leading ,. TYPE).value() gets around most common problems w/ special characters in XML.