Combining SQL Server Stuff statements to get single rows - sql

I have an interesting problem. I have two T-SQL STUFF statements. In this example I have used union. This displays the results in two rows.
What I’d like to do is to combine these results so that it returns a single row. I am using SQL Server 2008 R2. The solution should be able to copy with either stuff statement returning null.
Is there a way to achieve this?
Here is the code
SELECT
(SELECT
STUFF((SELECT (' ' + mod_orders.mod_no + '<br>')
FROM mod_serials
INNER JOIN mod_orders ON mod_serials.mod_id = mod_orders.mod_id
INNER JOIN mod_order_items ON mod_orders.mod_id = mod_order_items.mod_id AND mod_order_items.item_id = 170
WHERE
(mod_serials.serial_id = 62 AND
mod_serials.date_implemented IS NOT NULL)
ORDER BY mod_serials.serial_id
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, ''))
UNION
SELECT
(SELECT
STUFF((SELECT (' ' + char_data_mv.char_value + '<br>')
FROM char_data_mv
WHERE (char_data_mv.object_id = 62 AND char_data_mv.char_id = 177)
ORDER BY char_data_mv.row_id
FOR XML PATH(''), TYPE ).value('.','nvarchar(max)'), 1, 1, '')) as [Impl]
Example data:
Row1 - 00001<br> 00005<br>
Row2 - PMB 12345<br>
Combined results would be:
00001<br> 00005<br> PMB 12345<br>

The UNION statement returns all of the rows from the first table, and then all of the rows from the second table together in one big table - so this is why you are getting 2 rows.
Instead, you can just add 2 strings together with the '+' operator. You also want to return a string if the other is null, and adding nulls begets nulls. So use the ISNULL function to give an empty string instead.
SELECT
ISNULL((SELECT STUFF
((SELECT
(' ' + mod_orders.mod_no + '<br>')
FROM mod_serials
INNER JOIN mod_orders ON mod_serials.mod_id = mod_orders.mod_id
INNER JOIN mod_order_items ON mod_orders.mod_id = mod_order_items.mod_id AND mod_order_items.item_id = 170
WHERE (mod_serials.serial_id = 62 AND mod_serials.date_implemented IS NOT NULL)
ORDER BY mod_serials.serial_id FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 1, '')),'')
+
ISNULL((SELECT STUFF
((SELECT
(' ' + char_data_mv.char_value + '<br>')
FROM char_data_mv
WHERE (char_data_mv.object_id = 62 AND char_data_mv.char_id = 177)
ORDER BY char_data_mv.row_id FOR XML PATH(''), TYPE ).value('.','nvarchar(max)'), 1, 1, '')),'') as [Impl]
Note that you need to remove bothe the UNION and also the first SELECT after it - which is creating the second table.

Related

SQL how to put values on one line

It is necessary that in the second column in a single line should be all related accounts.
This shows an error
Conversion failed when converting the varchar value ',' to data type int.
SELECT [UserID],
STUFF((SELECT ', ' + UserID
FROM #RelatedIDs
WHERE (UserID = t.UserID)
FOR XML PATH('')) ,1,1,'') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID
You could cast the integer value to a varchar:
SELECT [UserID],
STUFF((SELECT ',' + CAST(UserID as VARCHAR(100))
FROM #RelatedIDs
WHERE (UserID = t.UserID)
FOR XML PATH('')) ,1,1,'') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID
If you are running a recent version of SQL Server (2017 or higher), you can get the same result in a much less obsfucated manner with string_agg():
SELECT t.UserID, STRING_AGG(r.UserID, ',') RelIDs
FROM #RelatedIDs t
INNER JOIN #RelatedIDs r on r.UserID = t.UserID
GROUP BY t.UserID
With the query put this way, it is plain to see that it makes little sense. The self-join operates on the same column as the one that defines the group, so this will just generate a list of identical UserIDs in column RelIDs (one per occurence of the given UserID in the original query).
Use CONCAT() instead of +:
SELECT [UserID],
STUFF((SELECT CONCAT(', ', UserID)
FROM #RelatedIDs ri
WHERE ri.UserID = t.UserID
FOR XML PATH('')
), 1, 2, '') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID;
Notice that I also changed the stuff arguments from 1, 1 to 1, 2. That is because you have a space after the comma.
Your correlation clause also suggests that the results will not be very interesting, when you get this to work.

Find out Equal row set

I have a number of row base on plan and plan detail, I want to find out the same row set with other detail like plan 1 has 3 rows of data in detail table so need to find out the same rows for another plan. I have some sample data with this post may be more helpful to understand my problem. below is the Sample data Image, iwan to group by the record but not on a single row base on the full row set base on
PlanId, MinCount, MaxCount and CurrencyId
my expected data is below
I had tried to do with Some lengthy process like append all data in a single row and compare with other data, but it seems very lengthy process and takes to much time for 100 records I have an approx 20000 records in an actual database so not a good solution, please suggest me some thought
This would be trivial in MS Sql Server 2017 by using STRING_AGG.
But in 2012 one of the methods is to use the FOR XML trick.
SELECT PlanId, StairCount, MinCount, MaxCount, CurrencyId,
STUFF((
SELECT CONCAT(',', t1.AccountId)
FROM YourTable t1
WHERE t1.PlanId = t.PlanId
AND t1.StairCount = t.StairCount
AND t1.MinCount = t.MinCount
AND t1.MaxCount = t.MaxCount
AND t1.CurrencyId = t.CurrencyId
ORDER BY t1.AccountId
FOR XML PATH('')), 1, 1, '') AS AccountIdList
FROM YourTable t
GROUP BY PlanId, StairCount, MinCount, MaxCount, CurrencyId
Test here
Your desired result is rather hard to produce in SQL Server. The simplest method requires two levels of string concatenation:
with t as (
select a.account_id,
stuff( (select '[' +
convert(varchar(255), staircount) + ',' +
convert(varchar(255), mincount) + ',' +
convert(varchar(255), maxcount) + ',' +
convert(varchar(255), currencyid) +
']'
from t
where t.account_id = a.account_id
order by staircount
for xml path ('')
), 1, 1, '') as details
from (select distinct account_id from t) a
)
select d.details,
stuff( (select cast(varchar(255), account_id) + ','
from t
where t.details = d.details
for xml path ('')
), 1, 1, '') as accounts
from (select distinct details from t) d;
This isn't exactly your output, but it might be good enough for your problem.

SQL - Combine Multiple Columns with Multiple Rows into one row

What I'm trying to do:
I have records in a SQL table where there are 5 columns and thousands of rows.
The rows share duplicate data (i.e. account number) but what makes each unique is that data in one of the columns is different.
As an example:
col1|col2|col3|col4|col5
------------------------
123|abc|456|def|789
123|abc|456|def|date
But the columns can have different values, not necessarily always in column 5.
Here's what I started with:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid]
,te.[char1]
,te.[date]
,te.[date2]
,te.[char2]
FROM D AS te
INNER JOIN D AS tue ON tue.[accountid] = te.[accountid]
WHERE tue.[accountid] = ue.[accountid]
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D AS ue
GROUP BY ue.[accountid]
But I get a monster long string that includes the duplicate rows in one column. I'm not sure what else to try so any insight would be appreciated.
If I had to guess, you have an unnecessary self join in the subquery:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid], te.[char1], te.[date], te.[date2], te.[char2]
FROM D te
WHERE te.[accountid] = ue.[accountid]
FOR XML path(''), type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D ue
GROUP BY ue.[accountid];
You might also want SELECT DISTINCT in the subquery.
Use UNION to get rid of all the duplicate values and use your FOR XML PATH on the output to append it to a single string:
SELECT TOP (15) stuff((
SELECT ', ' + CAST(te.[accountid] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char1] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date2] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char2] AS varchar(255)) FROM D
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
Untested, treat as pseudo-code to give the general idea.

SQL Declare Query only returns 1 row result

I'm trying to get a result from our db with one identifier (ArtNr) and one big chunk of text that also contains returns (Edit),
I also want the text from the (Edit) column to be stringed?
(like this: "texttexttext" it's the "" I need in the beginning and end of the text)
I have come up with the query below but it only gives 1 row in reply (should be like 4000) and it does of course not give me the Edit captioned with "" since I have no clue how to do it :)
Thanks in advance! /Christian
USE MSPes2t
DECLARE #result nvarchar(max) , #test nvarchar(255)
SELECT #result = AR.Edit , #test = AR.ArtNr
FROM DBO.AR
WHERE AR.ArtNr = '%'
ORDER BY AR.ArtNr
SELECT #test AS artnr,
#result AS edit
This is the result im searching for:
Like this:
ArtNr | Edit
------+----------------------------------------
12001 | "edit"
12002 | "edit"
28001 | "edit"
The following query should give you the desired results (although I am not exactly sure how you want to delimit your edits, I have used a semi-colon):
SELECT ArtNr = '12001',
Edit = STUFF(( SELECT ';"' + ar.Edit + '"'
FROM dbo.AR
WHERE AR.ArtNr = '12001'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
A full explanation of how using the xml extension FOR XML PATH() works to concanate rows into a single field can be found in this answer
If you need to get the edits concatenated for more than one ArtNo then you could use somethig like:
SELECT ar.ArtNr,
Edit = STUFF(( SELECT ';"' + ar2.Edit + '"'
FROM dbo.AR AS ar2
WHERE AR2.ArtNr = ar.ArtNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM dbo.Ar
WHERE ar.ArtNr IN ('12001', '12002')
GROUP BY ar.ArtNr;
SIMPLE WORKING EXAMPLE
DECLARE #AR TABLE (ArtNr VARCHAR(5), Edit NVARCHAR(MAX));
INSERT #AR (ArtNr, Edit)
VALUES ('12001', 'editeditedit'), ('12001', 'edit2 edit2'),
('12001', 'edit3'), ('12002', 'edit2 edit2');
SELECT ar.ArtNr,
Edit = STUFF(( SELECT ';"' + ar2.Edit + '"'
FROM #AR AS ar2
WHERE AR2.ArtNr = ar.ArtNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #AR AS ar
WHERE ar.ArtNr IN ('12001', '12002')
GROUP BY ar.ArtNr;
Gives:
ArtNr | Edit
------+----------------------------------------
12001 | "editeditedit";"edit2 edit2";"edit3"
12002 | "editeditedit"
EDIT
Based on your required output I think your query is as simple as concatenating " to either end of your edit field:
SELECT AR.ArtNr,
'"' + AR.edit + '"' AS Edit
FROM DBO.AR
WHERE AR.ArtNr = '%'
ORDER BY ArtNr;
In SQL Server 2012 and later you can use CONCAT but it doesn't offer much advantage in this scenario.

How to combine two rows with comma separator in sql server 2008

I tried as shown below
SELECT SUBSTRING(
(
SELECT td.Text + ', ' AS 'data()',
Tda.FirmID
FROM tblData1 tda
INNER JOIN tblData2 Td
ON Tda.ID = Td.ID
GROUP BY
Tda.Enable1,
Tda.ID,
Td.Text
HAVING ISNULL(Enable1, 0) = 1
FOR XML PATH('')
),
1,
10000
) AS Csv
Output:
Landlord Tenant, <FirmID>1</FirmID>NJ Traffic, <FirmID>1</FirmID>
Expected Output: Should be in table format
csv FirmID
Landlord Tenant, NJ Traffic 1
I got the results by using COALESCE
DECLARE #Text VARCHAR(MAX)
DECLARE #ID NUMERIC(18,0)
SELECT #Text = COALESCE(#Text + ', ', '') +
CAST(td.Text AS VARCHAR(MAX)),#ID = MAX(tda.FirmID)
FROM tblData1 tda
INNER JOIN tblData2 Td
ON Tda.ID=Td.ID
GROUP BY
Tda.Enable,
Tda.FirmID,Td.Text
HAVING ISNULL(Enable,0)=1
SELECT #Text AS Text,#ID AS ID
OutPut:
text ID
Landlord Tenant, NJ Traffic 1
Leave out the aliases in the subquery and try this:
SELECT STUFF((SELECT ', ' + td.Text
FROM tblData2 Td
WHERE ISNULL(Enable1, 0) = 1
FOR XML PATH('')
), 1, 2, '') AS Csv,
MAX(Tda.FirmID)
FROM tblData1 tda;
You cannot select multiple columns in the subquery either. After all, the purpose is to combine values into a single column. Multiple values in the select create one column, but with multiple XML tags within the string.
You also do not need the group by in the subquery.