SQL Server how to turn number of rows to column value? - sql-server

I have three rows return from a table as below:
select ID
from service
Results:
ID
--
1
2
3
How can I return output like below:
count | IDs
-------+----------
3 | 1,2,3

hope this helps
select (select Count(*) from service)+' | '+ SELECT STUFF
(
(
SELECT ',' + s.FirstName
FROM Employee s
ORDER BY s.FirstName FOR XML PATH('')
),
1, 1, ''
) AS Employees)

If you are going to be work with stuff() function then you will no need to subquery for count of ids
select count(1) count,
stuff(
(select ','+cast(id as varchar) from table for xml path('')),
1,1,'') Ids
from table

Related

Grouping of columns with comma separated and padding extra blanks in SQL Server

I have a table with similar data as below
Label Value ColorCode
--------------------------
M1 0.5 #C71585
M1 1.5 #808080
M2 1 #C71585
M2 2 #C71585
M2 1.6 #FFC0CB
M3 3 #9400D3
I want the data to be converted as below:
Label Data
-------------------------------------------
M1 0.5,"#C71585",1.5,"#808080","",""
M2 1,"#C71585",2,"#C71585",1.6,"#FFC0CB"
M3 3,"#9400D3","","","",""
Explanation: For each Label , I need to count the no of values in the table and pick the highest count.In this example , it is for M2 with count 3.
Now I have to append value and ColorCode columns with comma separated based on label. That part I have already done. The problem here is I need to be able to append the extra ""(double quotes) for which the count is less.
Example, for M1 , the count is 2(which is 1 less than the max count), So I have to append 1*2 "" in order to match with my max count label(M2). Similarly for M3, the count is 1(which is 2 less than the max count), so I have to append 2*2 "" (double quotes) to match with M2.
This is the query I have written to get the comma separated data.
SELECT
T1.Label,
STUFF((SELECT ', ' + CAST(T.Value AS VARCHAR) + ',"' + T.ColorCODE + '"'
FROM Table1 T
WHERE T.Label = T.Label
ORDER BY T.Label
FOR XML PATH('')), 1, 2, '') AS Data
FROM Table1 T1
GROUP BY T1.Label
ORDER BY T1.Label
Can any one tell me how to append the extra double quotes as explained above.
Thanks a lot in advance!!
PS: I'm using SQL Server 2014 Express Edition
Use Replicate. I just edited a bit your query
SELECT
T1.Label
, STUFF((
SELECT ',' + CAST(T.Value AS VARCHAR) + ',"' + T.ColorCODE + '"'
FROM Table1 T
WHERE T.Label = T1.Label
ORDER BY T.Label
FOR XML PATH('')
),1,1,'') + replicate(',"",""', max(count(*)) over () - count(*)) AS Data
FROM Table1 T1
GROUP BY T1.Label
Try this code :
SELECT
T1.Label
, STUFF((
SELECT ',' + CAST(T.Value AS VARCHAR) + ',"' + T.ColorCODE + '"'
FROM #Table T
WHERE T.Label = T1.Label
ORDER BY T.Label
FOR XML PATH('')
),1,1,'') AS Data
FROM #Table T1
GROUP BY T1.Label
ORDER BY T1.Label
Another alternative solution for you !---
Create Data
CREATE TABLE TestC
(
Label VARCHAR(10)
,Value DECIMAL(10,2)
,ColorCode VARCHAR(20)
)
GO
INSERT INTO TestC VALUES
('M1',0.5,'#C71585'),
('M1',1.5,'#808080'),
('M2',1 ,'#C71585'),
('M2',2 ,'#C71585'),
('M2',1.6,'#FFC0CB'),
('M3',3 ,'#9400D3')
GO
SOLUTION
;WITH CTE1 AS
(
SELECT Label , MAX(COUNT(*)) OVER() - COUNT(*) c FROM TestC
GROUP BY Label
)
,CTE2 AS
(
SELECT Label , CONCAT ( Value , ',"' , ColorCode , '"') Vals
FROM TestC
UNION ALL
SELECT c.Label , '""' Vals FROM CTE1 c
INNER JOIN master..spt_values m ON c.c >= m.number
WHERE m.type = 'p' AND c.c > 0
)
SELECT c.Label ,
STUFF((
SELECT ',' + Vals
FROM CTE2 c1
WHERE c1.Label = c.Label
ORDER BY c1.Label
FOR XML PATH('')
),1,1,'') cData
FROM CTE2 c
GROUP BY c.Label
OUTPUT
Label cData
---------- ---------------------------------------------------------
M1 0.50,"#C71585",1.50,"#808080","",""
M2 1.00,"#C71585",2.00,"#C71585",1.60,"#FFC0CB"
M3 3.00,"#9400D3","","",""
(3 rows affected)

How can I get count of column a into the column b in SQL Server?

I have this data into the testDB on SQL Server:
I want write query to get this data from the table:
1 --------> 3
2 --------> 2
How can I do this?
Thanks.
and how can i get this data from up table?
1----->A,B,C
2----->D,E
or else use Window function in sql server:
Case 1:
select count(column x) over(partition by column x) as cnt,
column x,
column y from table
You can use this FOR XML PATH & STUFF trick for CASE 2:
SELECT
b.columnX,
COUNT(*) as cnt,STUFF( (SELECT ',' + a.column y
FROM table a
where a.column x = b.coulmn x
FOR XML PATH('')),
1, 1, '')
FROM table b
GROUP BY b.columnX
A simple GROUP BY would do the trick.
SELECT
columnX,
COUNT(*)
FROM <YourTable>
GROUP BY columnX
For the other question:
SELECT
columnX,
STUFF((
SELECT ',' + columnY
FROM YourTable
WHERE columnX = t.columnX
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)')
,1 ,1 , '')
FROM YourTable t
GROUP BY t.columnX

Concatenate column values against another column group

May be this is odd but i need to concatenate values of ActionId to corresponding group of roleId and Order by ActionID is must., some thing like
ActionID RoleId
"1357" 1
"2468" 2
Here is what i have currently, I am looking for GROUP_CONCAT equivalent in MS SQL.
select av.ActionId, ra.RoleId from RoleAction ra join ActionValue av
on ra.ActionId = av.ActionId order by av.ActionId
ActionID RoleId
1 1
3 1
5 1
7 1
4 2
2 2
6 2
8 2
Is there way to do that? Thanks in advance.
You can make it work using FOR XML PATH('') and an inner query:
SELECT DISTINCT T1.RoleID,
(SELECT '' + ActionID
FROM RoleAction T2
WHERE T1.RoleID = T2.RoleID
ORDER BY ActionID
FOR XML PATH(''))
FROM RoleAction T1
This should work:
WITH CTE_A AS
(
select av.ActionId, ra.RoleId from RoleAction ra join ActionValue av
on ra.ActionId = av.ActionId
)
SELECT DISTINCT A.RoleId,
(SELECT '' +
CAST(B.ActionId AS varchar(10))
FROM CTE_A B
WHERE B.RoleID = A.RoleID
FOR XML PATH('')) AS ActionID
FROM CTE_A A
GROUP BY A.RoleID

Can I get comma separated values from sub query? If not, how to get this done?

I have a table
Create table Country_State_Mapping
(
Country nvarchar(max),
State nvarchar(max)
)
With 5 records.
Insert into Country_State_Mapping values('INDIA', 'Maharastra')
Insert into Country_State_Mapping values('INDIA', 'Bengal')
Insert into Country_State_Mapping values('INDIA', 'Karnatak')
Insert into Country_State_Mapping values('USA', 'Alaska')
Insert into Country_State_Mapping values('USA', 'California')
I need to write a SQL query which will have give me 2 records/2 columns as below.
1st column Contry and second AllStates
1 record(2 columns) will be
India and Maharastra,Bengal,Karnatak
2nd
USA and Alaska,California
I tried I like this
select distinct
OutTable.Country,
(select State
from Country_State_Mapping InnerTable
where InnerTable.Country = OutTable.Country)
from Country_State_Mapping AS OutTable
but did not work...
SELECT Country, AllStates =
STUFF((SELECT ', ' + State
FROM Country_State_Mapping b
WHERE b.Country = a.Country
FOR XML PATH('')), 1, 2, '')
FROM Country_State_Mapping a
GROUP BY Country
This is a bit nasty and potentially slow if the number of records in the Country_State_Mapping table is large but it does get the job done. It uses the recursive feature of Common Table Expressions introduced in SQL 2005.
;WITH Base
AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY Country ORDER BY Country, [State] DESC) AS CountryRowId,
ROW_NUMBER() OVER (ORDER BY Country, [State]) AS RowId,
Country,
[State]
FROM Country_State_Mapping
),
Recur
AS
(
SELECT
CountryRowId,
RowId,
Country,
[State]
FROM Base
WHERE RowId = 1
UNION ALL
SELECT
B.CountryRowId,
B.RowId,
B.Country,
CASE WHEN R.Country <> B.Country THEN B.[State] ELSE R.[State] + ',' + B.[State] END
FROM Recur R
INNER JOIN Base B
ON R.RowId + 1 = B.RowId
)
SELECT *
FROM Recur
WHERE CountryRowId = 1
OPTION (MAXRECURSION 0)--Dangerous

SQL Server - Include NULL using UNPIVOT

UNPIVOT will not return NULLs, but I need them in a comparison query. I am trying to avoid using ISNULL the following example (Because in the real sql there are over 100 fields):
Select ID, theValue, column_name
From
(select ID,
ISNULL(CAST([TheColumnToCompare] AS VarChar(1000)), '') as TheColumnToCompare
from MyView
where The_Date = '04/30/2009'
) MA
UNPIVOT
(theValue FOR column_name IN
([TheColumnToCompare])
) AS unpvt
Any alternatives?
To preserve NULLs, use CROSS JOIN ... CASE:
select a.ID, b.column_name
, column_value =
case b.column_name
when 'col1' then a.col1
when 'col2' then a.col2
when 'col3' then a.col3
when 'col4' then a.col4
end
from (
select ID, col1, col2, col3, col4
from table1
) a
cross join (
select 'col1' union all
select 'col2' union all
select 'col3' union all
select 'col4'
) b (column_name)
Instead of:
select ID, column_name, column_value
From (
select ID, col1, col2, col3, col4
from table1
) a
unpivot (
column_value FOR column_name IN (
col1, col2, col3, col4)
) b
A text editor with column mode makes such queries easier to write. UltraEdit has it, so does Emacs. In Emacs it's called rectangular edit.
You might need to script it for 100 columns.
It's a real pain. You have to switch them out before the UNPIVOT, because there is no row produced for ISNULL() to operate on - code generation is your friend here.
I have the problem on PIVOT as well. Missing rows turn into NULL, which you have to wrap in ISNULL() all the way across the row if missing values are the same as 0.0 for example.
I ran into the same problem. Using CROSS APPLY (SQL Server 2005 and later) instead of Unpivot solved the problem. I found the solution based on this article An Alternative (Better?) Method to UNPIVOT
and I made the following example to demonstrate that CROSS APPLY will NOT Ignore NULLs like Unpivot.
create table #Orders (OrderDate datetime, product nvarchar(100), ItemsCount float, GrossAmount float, employee nvarchar(100))
insert into #Orders
select getutcdate(),'Windows',10,10.32,'Me'
union
select getutcdate(),'Office',31,21.23,'you'
union
select getutcdate(),'Office',31,55.45,'me'
union
select getutcdate(),'Windows',10,null,'You'
SELECT OrderDate, product,employee,Measure,MeasureType
from #Orders orders
CROSS APPLY (
VALUES ('ItemsCount',ItemsCount),('GrossAmount',GrossAmount)
)
x(MeasureType, Measure)
SELECT OrderDate, product,employee,Measure,MeasureType
from #Orders orders
UNPIVOT
(Measure FOR MeasureType IN
(ItemsCount,GrossAmount)
)AS unpvt;
drop table #Orders
or, in SQLServer 2008 in shorter way:
...
cross join
(values('col1'), ('col2'), ('col3'), ('col4')) column_names(column_name)
Using dynamic SQL and COALESCE, I solved the problem like this:
DECLARE #SQL NVARCHAR(MAX)
DECLARE #cols NVARCHAR(MAX)
DECLARE #dataCols NVARCHAR(MAX)
SELECT
#dataCols = COALESCE(#dataCols + ', ' + 'ISNULL(' + Name + ',0) ' + Name , 'ISNULL(' + Name + ',0) ' + Name )
FROM Metric WITH (NOLOCK)
ORDER BY ID
SELECT
#cols = COALESCE(#cols + ', ' + Name , Name )
FROM Metric WITH (NOLOCK)
ORDER BY ID
SET #SQL = 'SELECT ArchiveID, MetricDate, BoxID, GroupID, ID MetricID, MetricName, Value
FROM
(SELECT ArchiveID, [Date] MetricDate, BoxID, GroupID, ' + #dataCols + '
FROM MetricData WITH (NOLOCK)
INNER JOIN Archive WITH (NOLOCK)
ON ArchiveID = ID
WHERE BoxID = ' + CONVERT(VARCHAR(40), #BoxID) + '
AND GroupID = ' + CONVERT(VARCHAR(40), #GroupID) + ') p
UNPIVOT
(Value FOR MetricName IN
(' + #cols + ')
)AS unpvt
INNER JOIN Metric WITH (NOLOCK)
ON MetricName = Name
ORDER BY MetricID, MetricDate'
EXECUTE( #SQL )
I've found left outer joining the UNPIVOT result to the full list of fields, conveniently pulled from INFORMATION_SCHEMA, to be a practical answer to this problem in some contexts.
-- test data
CREATE TABLE _t1(name varchar(20),object_id varchar(20),principal_id varchar(20),schema_id varchar(20),parent_object_id varchar(20),type varchar(20),type_desc varchar(20),create_date varchar(20),modify_date varchar(20),is_ms_shipped varchar(20),is_published varchar(20),is_schema_published varchar(20))
INSERT INTO _t1 SELECT 'blah1', 3, NULL, 4, 0, 'blah2', 'blah3', '20100402 16:59:23.267', NULL, 1, 0, 0
-- example
select c.COLUMN_NAME, Value
from INFORMATION_SCHEMA.COLUMNS c
left join (
select * from _t1
) q1
unpivot (Value for COLUMN_NAME in (name,object_id,principal_id,schema_id,parent_object_id,type,type_desc,create_date,modify_date,is_ms_shipped,is_published,is_schema_published)
) t on t.COLUMN_NAME = c.COLUMN_NAME
where c.TABLE_NAME = '_t1'
</pre>
output looks like:
+----------------------+-----------------------+
| COLUMN_NAME | Value |
+----------------------+-----------------------+
| name | blah1 |
| object_id | 3 |
| principal_id | NULL | <======
| schema_id | 4 |
| parent_object_id | 0 |
| type | blah2 |
| type_desc | blah3 |
| create_date | 20100402 16:59:23.26 |
| modify_date | NULL | <======
| is_ms_shipped | 1 |
| is_published | 0 |
| is_schema_published | 0 |
+----------------------+-----------------------+
Writing in May'22 with testing it on AWS Redshift.
You can use a with clause where you can coalesce the columns where nulls are expected. Alternatively, you can use coalesce in the select statement prior to the UNPIVOT block.
And don't forget to alias with the original column name (Not following won't break or violate the rule but would save some time for coffee).
Select ID, theValue, column_name
From
(select ID,
coalesce(CAST([TheColumnToCompare] AS VarChar(1000)), '') as TheColumnToCompare
from MyView
where The_Date = '04/30/2009'
) MA
UNPIVOT
(theValue FOR column_name IN
([TheColumnToCompare])
) AS unpvt
OR
WITH TEMP1 as (
select ID,
coalesce(CAST([TheColumnToCompare] AS VarChar(1000)), '') as TheColumnToCompare
from MyView
where The_Date = '04/30/2009'
)
Select ID, theValue, column_name
From
(select ID, TheColumnToCompare
from MyView
where The_Date = '04/30/2009'
) MA
UNPIVOT
(theValue FOR column_name IN
([TheColumnToCompare])
) AS unpvt
I had your same problem and this is
my quick and dirty solution :
your query :
select
Month,Name,value
from TableName
unpivot
(
Value for Name in (Col_1,Col_2,Col_3,Col_4,Col_5
)
) u
replace with :
select Month,Name,value from
( select
isnull(Month,'no-data') as Month,
isnull(Name,'no-data') as Name,
isnull(value,'no-data') as value from TableName
) as T1
unpivot
(
Value
for Name in (Col_1,Col_2,Col_3,Col_4,Col_5)
) u
ok the null value is replaced with a string, but all rows will be returned !!
ISNULL is half the answer. Use NULLIF to translate back to NULL. E.g.
DECLARE #temp TABLE(
Foo varchar(50),
Bar varchar(50) NULL
);
INSERT INTO #temp( Foo,Bar )VALUES( 'licious',NULL );
SELECT * FROM #temp;
SELECT
Col,
NULLIF( Val,'0Null' ) AS Val
FROM(
SELECT
Foo,
ISNULL( Bar,'0Null' ) AS Bar
FROM
#temp
) AS t
UNPIVOT(
Val FOR Col IN(
Foo,
Bar
)
) up;
Here I use "0Null" as my intermediate value. You can use anything you like. However, you risk collision with user input if you choose something real-world like "Null". Garbage works fine "!##34())0" but may be more confusing to future coders. I am sure you get the picture.

Resources