Convert a column of Rows to only one column column in SQL Server query - sql

I have this query in SQL Server 2012 :
select
tblplantitle.id, tblplantoproductpayment.productid
from
tblplantitle
inner join
tblplan on tblplan.plantitleid = tblplantitle.id
inner join
tblplantoproductpayment on tblplantoproductpayment.planid = tblplan.id
group by
tblplantitle.id, tblplantoproductpayment.productid
The result is like this :
id productid
1 1
1 2
1 3
1 10
2 5
2 1
3 4
3 11
but I want this result :
1 1,2,3,10
2 5,1
3 4,11
How can I get that result ?

Try this:
WITH cte as
(
select
tblplantitle.id, tblplantoproductpayment.productid
from
tblplantitle
inner join
tblplan on tblplan.plantitleid = tblplantitle.id
inner join
tblplantoproductpayment on tblplantoproductpayment.planid = tblplan.id
group by
tblplantitle.id, tblplantoproductpayment.productid
)
SELECT id, productid =
STUFF((SELECT ', ' + productid
FROM cte b
WHERE b.id= a.id
FOR XML PATH('')), 1, 2, '')
FROM cte a
GROUP BY id

Use the below query for the desired output.
SELECT tblplantitle.id
, STUFF((SELECT ', ' + CAST(tblplantoproductpayment.productid AS VARCHAR(10)) [text()]
FROM tblplantoproductpayment
WHERE tblplantoproductpayment.planid = tblplan.id
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,' ') List_ProductIds
FROM tblplantitle
INNER JOIN tblplan on tblplan.plantitleid = tblplantitle.id
GROUP BY tblplantitle.id

Related

SQL Server mulitple results to one row by ID (Version 2012)

Consider this query and result set:
Select udbA.userId, d.dbName
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid
Order By udbA.userId
1 az
1 nc_west
1 bsc_mo
1 NS_002
What I am looking for is a way to flatten this into one record. I know I can do it with a temp table and select into, but I was curious to see if a query could do it directly. A user could have up to 15 databases available to them.
Looking for results like below ( 2 columns -- userid and the database names ):
userid dbname
1 az nc_west bsc_mo NS_002
SQL Server Version: Microsoft SQL Server 2012 (SP3) (KB3072779) - 11.0.6020.0 (X64) Oct 20 2015 15:36:27 Copyright (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.3 (Build 9600: ) (Hypervisor)
Assuming you want a space-delimited list of database names:
DECLARE #Access table ( userId int, dbName varchar(50) );
INSERT INTO #Access VALUES
( 1, 'az' ), ( 1, 'nc_west' ), ( 1, 'bsc_mo' ), ( 1, 'NS_002' );
SELECT DISTINCT
ax.userId, db.list
FROM #Access AS ax
OUTER APPLY (
SELECT LTRIM ( (
SELECT ' ' + dbName AS "text()" FROM #Access AS x WHERE x.userId = ax.userId
FOR XML PATH ( '' )
) ) AS list
) AS db;
Returns
+--------+--------------------------+
| userId | list |
+--------+--------------------------+
| 1 | az nc_west bsc_mo NS_002 |
+--------+--------------------------+
For a comma-delimited list:
SELECT DISTINCT
ax.userId, db.list
FROM #Access AS ax
OUTER APPLY (
SELECT STUFF ( (
SELECT ',' + dbName AS "text()" FROM #Access AS x WHERE x.userId = ax.userId
FOR XML PATH ( '' )
), 1, 1, '' ) AS list
) AS db;
Returns
+--------+--------------------------+
| userId | list |
+--------+--------------------------+
| 1 | az,nc_west,bsc_mo,NS_002 |
+--------+--------------------------+
You need GROUP BY and row_number as follows:
select userId,
max(case when rn = 1 then dbName end) as val1,
max(case when rn = 2 then dbName end) as val2,
max(case when rn = 3 then dbName end) as val3,
max(case when rn = 4 then dbName end) as val4
from
(Select udbA.userId, d.dbName,
row_number() over (partition by udbA.userId order by d.dbName) as rn
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid ) t
group by userId
-- update
You just need two columns then use the STRING_AGG as follows:
Select udbA.userId,
string_agg(d.dbName, ' ') within group (order by d.dbName) as dbName
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid
group by udbA,userId
-- For SQL server 2012
Select distinct udbA.userId,
STUFF((SELECT distinct '' + d.dbName
from dbList d
where d.dbid = udbA.dbid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'') AS dbid
FROM user_db_access udbA
WHERE EXISTS (SELECT 1 FROM dbList d
where d.dbid = udbA.dbid)
Or use CTE as follows:
with CTE as
(Select udbA.userId, d.dbName
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid)
Select distinct c.userId,
STUFF((SELECT distinct '' + cc.dbName
from cte cc
where c.dbid = cc.dbid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'') AS dbid
FROM cte c
Depending on your SQL Server version you can use STRING_AGG
Like this:
Select udbA.userId, string_agg(d.dbName, ', ') as name
from user_db_access udbA
Inner join dbList d on d.dbid = udbA.dbid
group by udbA.userId
If you are using an older version you already have answers here: How to make a query with group_concat in sql server
Like this:
select
udbA.userId,
stuff((
select cast(',' as varchar(max)) + d.dbName
from dbList d
where d.dbid = udbA.dbid
order by d.dbName
for xml path('')), 1, 1, '') as dbList
from
user_db_access udbA
inner join dbList dl on dl.dbid = udbA.dbid
order by
udbA.userId;

SQL Moving SUBSTRING select into INNER JOIN

I have this query
SELECT ID,
SUBSTRING(( SELECT DISTINCT ',' + CONVERT(varchar(10), CC.CompanyId)
FROM Company CC
INNER JOIN CompanyProducts NP2
ON CC.CompanyId = NP2.CompanyId
WHERE NP.CompanyProducts Id = NP2.PrimaryCompanyProducts Id
AND NP2.CompanyProducts Id <> NP2.CompanyProducts Id
FOR XML PATH('')),2,200000) AS CompanyIdList
FROM CompanyProducts NP
I would like to add the SELECT into an INNER JOIN which I will add to my select to check if the return is null or zero
it will be something like this
SELECT ID,
SUBSTRING(( SELECT DISTINCT ',' + CONVERT(varchar(10), CC.CompanyId)
FROM Company CC
INNER JOIN CompanyProducts NP2
ON CC.CompanyId = NP2.CompanyId
WHERE NP.CompanyProducts Id = NP2.PrimaryCompanyProducts Id
AND NP2.CompanyProducts Id <> NP2.CompanyProducts Id
FOR XML PATH('')),2,200000) AS CompanyIdList,
CompanyIdCount --this will be null or a real value
FROM cmp.CompanyProducts NP
INNER JOIN(SELECT DISTINCT A.CompanyProductsId,
(SELECT DISTINCT ',' + CONVERT(varchar(10), CC.CompanyId)
FROM Company CC
INNER JOIN CompanyProducts NP2
ON CC.CompanyId = NP2.CompanyId
WHERE NP.CompanyProducts Id = NP2.PrimaryCompanyProducts Id
AND NP2.CompanyProducts Id <> NP2.CompanyProducts Id ) AS CompanyIdCount
FROM cmp.CompanyProducts A
)E ON NP.CompanyNotificationId = E.CompanyNotificationId
How can I get the inner JOIN to incident null for no records or 1 record or more? THanks

SQL recursive query that gets all ancestors of an item

ID parent_id name
---------------------
1 2 first
2 4 second
3 3 third
4 5 fourth
5 - fifth
Ancestors list of first should be (2, 4, 5)
with name_tree as (
select id, parent_id, name
from the_unknown_table
where id = 1 -- this is the starting point you want in your recursion
union all
select c.id, c.parent_id, c.name
from the_unknown_table c
join name_tree p on p.parent_id = c.id -- this is the recursion
)
select *
from name_tree
where id <> 1; -- exclude the starting point from the overall result
SQLFiddle: http://sqlfiddle.com/#!3/87d0c/1
You can use something like this:
with parents as
(
select ID, parent_ID
from t
where parent_ID is not null
union all
select p.ID, t.parent_ID
from parents p
inner join t on p.parent_ID = t.ID
and t.parent_ID is not null
and t.ID <> t.parent_ID
)
select *
, parents = '(' + stuff
(
(
select ', ' + cast(p.parent_ID as varchar(100))
from parents p
where t.ID = p.ID
for xml path('')
), 1, 2, ''
) + ')'
from t
order by ID
SQL Fiddle with demo.
This combines two very common T-SQL techniques - using a CTE to get a hierarchy and using FOR XML PATH to get a CSV list.

SQL query with comma separated values

I have a requirement wherein I have 3 tables
ContactInfo
idContactinfo FirstName Lastname idCOmpanyid
---------------------------------------------------
1 Blah1 Blah1 1
2 Blah2 Blah2 1
3 Blah3 Blah3 1
4 Blah4 Blah4 1
ContactRole
idcontactrole IdContactRoleName idContactInfo
------------------------------------------------------
1 1 1
2 1 2
3 1 3
4 2 4
IdContactRoleLookup
idcontactRole RoleName
----------------------------------
1 Admin
2 Secretary
I need a query that gives me a result like this
idcontactrolename Contacts
--------------------------------------------------------
Admin Blah1,Blah1;Blah2,Blah2,Blah3,Blah3
Secretary Blah4,Blah4
This is what I have till now !!
SELECT l.sValue
, SUBSTRING((
SELECT C.sLast + ',' + C.sFirst + ';'
FROM contactinfo c
inner join contactrole crole
ON crole.idcontactinfo = c.idcontactinfo
WHERE crole.idcompany = '<<blah>>'
and c.idcompany = '<<blah>>'
FOR XML PATH('')
), 1, 1000000)
FROM CONTACTROLE CR
inner join contactinfo c on
cr.idcontactinfo = c.idcontactinfo
inner join lookupdata l on cr.idlookuprole = l.idlookupdata
where c.idcompany = '<<blah>>'
SELECT
RoleName,
STUFF(
(
SELECT ';' + b.FirstName + ', ' + b.LastName
FROM ContactRole a
INNER JOIN ContactInfo b
ON a.idContactInfo = b.idContactinfo
WHERE a.IdContactRoleName = r.idcontactRole
FOR XML PATH (''))
, 1, 1, '') AS NamesList
FROM IdContactRoleLookup r
GROUP BY idcontactRole, RoleName
SQLFiddle Demo
I Would do somethng like this, where you use the ID of the main QUERY into your SUBQUERY
SELECT
l.sValue
, SUBSTRING((
SELECT C.sLast + ',' + C.sFirst + ';'
FROM contactinfo c
inner join contactrole crole
ON crole.idcontactinfo = c.idcontactinfo
WHERE c.idcompany = '<<blah>>'
AND c.idcontactrole = CR.idcontactrole
FOR XML PATH('')
), 1, 1000000)
FROM CONTACTROLE CR
inner join lookupdata l on cr.idlookuprole = l.idlookupdata
This will give you all the role for one Compagnie. If the role does'nt exist you will have a NULL value.
You can use CROSS APPLY to get the result:
select distinct l.RoleName,
left(list, len(list) -1) list
FROM CONTACTROLE CR
inner join IdContactRoleLookup l
on cr.IdContactRoleName = l.idcontactRole
cross apply
(
select C1.Lastname + ',' + C1.FirstName + ';'
from CONTACTROLE CR1
inner join contactinfo c1
on cr1.idContactInfo = c1.idContactinfo
where cr.IdContactRoleName = cr1.IdContactRoleName
FOR XML PATH('')
) t2 (list)
See SQL Fiddle with Demo

SQL: Combining Rows with Some Conditions And Delete Rest of Them

I have a table like this;
Table: PROSPECT
ID CUSTOMER NUMBER
1 A XXXA1
1 B XXXA1
1 G XXX00
2 C XXX32
2 D XXX32
2 E XXX32
2 F XXX66
I want to combine rows' customer's name with ; where they has same ID and NUMBER. (and delete other rows.)
I want to it like this;
Table: PROSPECT
ID CUSTOMER NUMBER
1 A;B XXXA1
1 G XXX00
2 C;D;E XXX32
2 F XXX66
How can I do that?
Disclaimer
I agree with #Dems that you should not change your normalized data to this.
A better alternative might be to create a VIEW that presents you the data in the format you need.
SQL Statement
CREATE VIEW dbo.VIEW_PROSPECT AS
SELECT po.ID
, STUFF((SELECT '; ' + CUSTOMER FROM PROSPECT pi WHERE pi.ID = po.ID AND pi.N UMBER = po.NUMBER ORDER BY CUSTOMER FOR XML PATH('')),1,2,'') AS CUSTOMER
, po.NUMBER
FROM PROSPECT po
GROUP BY
po.ID
, po.NUMBER
Test script
;WITH PROSPECT(ID, CUSTOMER, NUMBER) AS (
SELECT 1, 'A', 'XXXA1'
UNION ALL SELECT 1, 'B', 'XXXA1'
UNION ALL SELECT 1, 'G', 'XXX00'
UNION ALL SELECT 2, 'C', 'XXX32'
UNION ALL SELECT 2, 'D', 'XXX32'
UNION ALL SELECT 2, 'E', 'XXX32'
UNION ALL SELECT 2, 'F', 'XXX66'
)
SELECT po.ID
, STUFF((SELECT '; ' + CUSTOMER FROM PROSPECT pi WHERE pi.ID = po.ID AND pi.NUMBER = po.NUMBER ORDER BY CUSTOMER FOR XML PATH('')),1,2,'') AS CUSTOMER
, po.NUMBER
FROM PROSPECT po
GROUP BY
po.ID
, po.NUMBER
ORDER BY
ID
, CUSTOMER
, NUMBER
SELECT DISTINCT ID, LEFT(T.CUSTOMER,LEN(T.CUSTOMER)-1), NUMBER
FROM PROSPECT A CROSS APPLY
(SELECT CUSTOMER + ';' AS [text()] FROM PROSPECT B
WHERE A.ID = B.ID and A.NUMBER = B.NUMBER
ORDER BY ID FOR XML PATH('')) T (CUSTOMER)