Dynamically evaluate an expression stored in a table column - sql

I have the following table
PnlId LineTotalisationId Designation Totalisation
1 A Gross Fees Formule A01+A02+A03+A04+A05
2 A01 GF1 Comptes B01+B02+B03+B04+B05
3 A02 GF2 Comptes C01+C02+C03+C04+C05
4 A03 GF3 Comptes 99991
5 A04 GF4 Comptes 99996
6 A05 GF5 Comptes 999995
14 B1 Perm Comptes 12+14+25
I am looking to get
If Designation=Formule in Totalisation LineTotalisationId name for PnlId contents number so I should concat Totalisation for all Totalisation else I will simply add the row
I try the following code
SELECT Ref.*,
CASE
WHEN Charindex('+', Ref.totalisation) > 0 THEN '+'
WHEN Charindex('/', Ref.totalisation) > 0 THEN '/'
WHEN Charindex('*', Ref.totalisation) > 0 THEN '*'
WHEN Charindex('*', Ref.totalisation) > 0 THEN '-'
END AS Operator
INTO ##ttresults
FROM [dbo].[pnlreference] Ref
WHERE [typetotalisation] = 'Formule'
AND totalisation <> ''
AND designation NOT LIKE '%[0-9]%'
AND designation != ''
SELECT split.linetotalisationid AS PNLParentId,
NULL AS Totalisation
INTO ##tempresults
FROM (SELECT tbl.designation,
tbl.linetotalisationid,
tbl.typetotalisation,
tbl.pnlid,
tbl.totalisation,
Cast('<t>'
+ Replace(tbl.totalisation, tbl.operator, '</t><t>')
+ '</t>' AS XML) x,
tbl.operator
FROM ##ttresults AS tbl) split
CROSS apply x.nodes('/t') t(c)
INNER JOIN [dbo].[pnlreference] Ref
ON Ref.linetotalisationid = t.c.value('.', 'nvarchar(255)')
WHERE Ref.designation LIKE '%[0-9]%'
OR Ref.designation = ''
GROUP BY split.linetotalisationid ;
The code above returns as result
PNLParentId
A
Is there a way to modify it to get totalisation as it is described ?

If your sample data represent your data, you can try below sql
Assuming Panel is your table name and the Line field only have 1 string character
Declare #sql nvarchar(max);
;
set #sql =
'with cte as
(
select
case
when patindex(''%[A-Z]%'',Line) > 0
then
substring(line,patindex(''%[A-Z]%'',Line),1)
end as LineId
,Totalisation
from panel
where line in (' +''''+ (select replace(a.totalisation,'+',''',''') from Panel a where Designation='formule' ) + '''' +')
union all
select
Line as LineId,
Totalisation
from panel
where line not in (' +''''+ (select replace(a.totalisation,'+',''',''') from Panel a where Designation='formule' ) + '''' +')
and Designation <> ''Formule''
'
+ ')
select
distinct
p.pnlId, p.Line, p.TotalisationId--, p.Designation ,P.Totalisation
,LineId, LTRIM(substring(stuff
(
(
select '' | '' + c2.Totalisation from cte c2 where c.LineId = c2.LineId for xml path('''')
)
,1,0,''''
),3,len(stuff
(
(
select '' | '' + c2.Totalisation from cte c2 where c.LineId = c2.LineId for xml path('''')
)
,1,0,''''
)))
) as Totalisation
from cte c
right join panel p on c.LineId = p.Line
where c.Totalisation is not null
'
;
exec(#sql)
/*
RESULT
pnlId Line TotalisationId LineId Totalisation
----------- ----- -------------- ------ --------------------------------------
1 A Gross Fees A 99999 | 99998 | 99991 | 99996 | 999995
14 B1 Perm B1 12+14+25
*/
UPDATED
TO use #roopesh Sample data B1 'Formule'
declare #formula nvarchar(max);
;
with cte as
(
select
distinct 1 as id, p.Totalisation
from
panel2 p
where Designation = 'formule'
)
select
distinct #formula = '''' + Replace(replace(substring(stuff
(
(
select ',' + c2.Totalisation from cte c2 where c.id = c2.id for xml path('')
)
,1,0,''
),2,len(stuff
(
(
select ',' + c2.Totalisation from cte c2 where c.id = c2.id for xml path('')
)
,1,0,''
))),',',''','''),'+',''',''') + ''''
from cte c
;
Declare #sql nvarchar(max);
;
set #sql =
'
;with cte as
(
select
case
when patindex(''%[A-Z]%'',Line) > 0
then
substring(line,patindex(''%[A-Z]%'',Line),1)
end as LineId
,Totalisation
from panel2
where line in (' + #formula +')
union all
select
Line as LineId,
Totalisation
from panel2
where line not in (' + #formula +')
and Designation <> ''Formule''
'
+ ')
select
distinct
p.pnlId, p.Line, p.TotalisationId--, p.Designation , p.totalisation
,LineId, Case when c.totalisation is null and p.designation=''Formule'' then p.totalisation
else
LTRIM(substring(stuff
(
(
select '' | '' + c2.Totalisation from cte c2 where c.LineId = c2.LineId for xml path('''')
)
,1,0,''''
),3,len(stuff
(
(
select '' | '' + c2.Totalisation from cte c2 where c.LineId = c2.LineId for xml path('''')
)
,1,0,''''
)))
)
end as Totalisation
from cte c
right join panel2 p on c.LineId = p.Line
where p.Designation = ''Formule''
'
;
exec(#sql)

This stored procedure comes as a solution for your problem. It is using cursor. May be there is a way to remove the cursor, but could not get till now. So got this solution.
CREATE Procedure [dbo].[spGetResult]
As
Begin
declare #curPNL cursor
declare #pnlid int
declare #Line varchar(10), #TotalisationId varchar(20), #Totalisation varchar(50)
declare #spresult table(PNLId int, Line varchar(10), TotalisationId varchar(20), result varchar(4000));
--declare the cursor
set #curPNL = cursor
for select PnlId, Line, TotalisationId, totalisation
from PNLTable where designation = 'Formule'
open #curPNL
Fetch Next From #curPNL into #pnlId, #Line, #TotalisationId, #Totalisation
While ##FETCH_STATUS = 0
Begin
declare #nsql nvarchar(4000);
declare #table table(tname varchar(50));
declare #result varchar(4000)
delete from #table
--get the totalisation data for specific column
set #nsql = 'select totalisation from PNLTable Where Line in (''' + replace(#Totalisation,'+',''',''') + ''')';
print 'Calling child'
insert into #table
exec(#nsql);
set #result = '';
if not exists (select 1 from #table)
Begin
set #result = replace(#Totalisation,'+','|')
End
else
Begin
--get the values of totalisation in a pipe separated string
select #result = case when #result = '' then '' else #result + '|' end + tname from #table;
End
--insert the values in the temporary table
insert into #spresult(PNLId, Line, TotalisationId, result)
select #pnlid, #Line, #TotalisationId, #result
Fetch Next From #curPNL into #pnlId, #Line, #TotalisationId, #Totalisation
End
close #curPNL
deallocate #curPNL
select * from #spresult;
End
Though the table structure was not very much clear to me. But I used the following script to create the table and insert the data.
CREATE TABLE [dbo].[PNLTable](
[PnlId] [int] NOT NULL,
[Line] [varchar](10) NULL,
[TotalisationId] [varchar](20) NULL,
[Designation] [varchar](20) NULL,
[Totalisation] [varchar](50) NULL,
PRIMARY KEY CLUSTERED
(
[PnlId] ASC
)
)
--insert data
INSERT [PNLTable]
([PnlId], [Line], [TotalisationId], [Designation], [Totalisation])
VALUES (1, N'A', N'Gross Fees', N'Formule', N'A01+A02+A03+A04+A05'), (2, N'A01', N'GF1', N'Comptes', N'99999')
,(3, N'A02', N'GF2', N'Comptes', N'99998'), (4, N'A03', N'GF3', N'Comptes', N'99991'), (5, N'A04', N'GF4', N'Comptes', N'99996')
, (6, N'A05', N'GF5', N'Comptes', N'999995'), (14, N'B1', N'Perm', N'Formule', N'12+14+25')

You have to generate a dynamic sql query using the formular from Designation=Formule instead of using a big static query. Then run the code by using EXEC.
The query could be something like this:
SELECT
(SELECT Totalisation WHERE Line = 'A01')
+
(SELECT Totalisation WHERE Line = 'A02')
+
(SELECT Totalisation WHERE Line = 'A03')
+
(SELECT Totalisation WHERE Line = 'A04')
+
(SELECT Totalisation WHERE Line = 'A05')
FROM [dbo].[pnlreference] AS Ref
WHERE Designation = 'Formule'
AND LEFT(Line, 1) = 'A'
Maybe split the formular content into single entries and operators and load it into a temp table wich contains the formular (perhabs A01 and A02, etc.) and a id.
Then generate the code: The code could be something like this:
EXEC 'SELECT
(SELECT Totalisation WHERE Line = ' + CAST((SELECT Formular FROM #formular WHERE id = 0) AS VARCHAR(MAX)) +')
' + CAST((SELECT Operator FROM #formular WHERE id = 1) AS VARCHAR(MAX)) +'
(SELECT Totalisation WHERE Line = ' + CAST((SELECT Formular FROM #formular WHERE id = 1) AS VARCHAR(MAX)) +')
' + CAST((SELECT Operator FROM #formular WHERE id = 2) AS VARCHAR(MAX)) +'
(SELECT Totalisation WHERE Line = ' + CAST((SELECT Formular FROM #formular WHERE id = 2) AS VARCHAR(MAX)) +')
' + CAST((SELECT Operator FROM #formular WHERE id = 3) AS VARCHAR(MAX)) +'
(SELECT Totalisation WHERE Line = ' + CAST((SELECT Formular FROM #formular WHERE id = 3) AS VARCHAR(MAX)) +')
' + CAST((SELECT Operator FROM #formular WHERE id = 4) AS VARCHAR(MAX)) +'
(SELECT Totalisation WHERE Line = ' + CAST((SELECT Formular FROM #formular WHERE id = 4) AS VARCHAR(MAX)) +')
FROM [dbo].[pnlreference] AS Ref
WHERE Designation = ''Formule''
AND LEFT(Line, 1) = ' + LEFT(CAST((SELECT Formular FROM #formular WHERE id = 0) AS VARCHAR(MAX)), 1);

Related

Sql join right table multiple row to column

Sql how to join right table multiple row to column
Table Application_detail
Id appl_name status
1. Abc. 1
2. DEF. 1
Table Approve_detail
applicant_id. Remark. Approveby
1. Appr by village Village
1. Appr by dist. District
I want to join like
Id. Applname. Status. Village_remark. District_remark
1. Abc. 1. Appr by village. Appr by dist
If you have hardcoded list of ApproveBys, you can do this:
SELECT ad.Id,
ad.appl_name AS Applname,
ad.status,
vil.Remark as Village_remark,
dist.Remark as Distric_remark
FROM Application_detail ad
JOIN Approve_detail vil
ON vil.applicant_id = ad.id
AND vil.Approveby = 'Village'
JOIN Approve_detail dist
ON dist.applicant_id = ad.id
AND dist.Approveby = 'District'
Otherwise you need to build a dynamic SQL. Like this:
-- Filling in initial data
SELECT *
INTO #Application_detail
FROM
(
SELECT 1 Id, 'Abc' appl_name, 1 Status
UNION ALL
SELECT 2, 'DEF', 1
) t
SELECT *
INTO #Approve_detail
FROM
(
SELECT 1 applicant_id, 'Appr by village' Remark, 'Village' Approveby
UNION ALL
SELECT 1, 'Appr by dist.', 'District'
) t
-- Start of the code
SELECT DISTINCT Approveby
INTO #Approvers
FROM #Approve_detail;
DECLARE #SQL NVARCHAR(MAX),
#SELECT NVARCHAR(MAX),
#FROM NVARCHAR(MAX),
#WHERE NVARCHAR(MAX);
SET #SELECT = 'SELECT ad.Id, ad.appl_name AS Applname, ad.status'
SET #FROM = '
FROM #Application_detail ad'
SET #WHERE = '
WHERE ad.id IN (SELECT applicant_id FROM #Approve_detail)'
SELECT #SELECT += ', t' + CAST(rn AS VARCHAR) + '.Remark AS ' + Approveby + '_remark',
#FROM += '
LEFT JOIN #Approve_detail t' + CAST(rn AS VARCHAR) + '
ON t' + CAST(rn AS VARCHAR) + '.applicant_id = ad.id
AND t' + CAST(rn AS VARCHAR) + '.Approveby = ''' + Approveby + ''''
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY Approveby) rn, Approveby FROM #Approvers
) t
SET #SQL= #SELECT + #FROM + #WHERE;
EXEC SP_EXECUTESQL #SQL

Using pivot in SQL Server not returning desired output

I have two tables like this:
**tblTagDescription**
and **tblDataLog**
Now I want to show record of any specific group and in one there might be same group for multiple id in tbltagdescription. And id of tbltagdescription is foreign key for tblDataLog as TagDescID.
Here 'Group1' has 10 ID as from 1 to 10. and there might be multiple record for these ID (from 1 to 10) in tbldatalog. I want these ID from 1 to as columns. For this I used pivot:
DECLARE #COlsID NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
IF OBJECT_ID('tempdb..##MYTABLE') IS NOT NULL
DROP TABLE ##MYTABLE
SELECT
#COlsID = COALESCE(#ColsID + '],[','') + CONVERT(NVARCHAR(5), z.TagDescID)
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID) z
SET #COlsID='[' + #COlsID + ']'
SET #SQL='select [DATE],SHIFT, ' + #COlsID + ' into ##MYTABLE from ( select [Date], Value,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)
Now when I execute this statement, I get an error:
Invalid column name 'TagDescID'
but there is this column in tbldatalog. How to solve this query?
You need TagDescID column in subquery.
DECLARE #COlsID NVARCHAR(MAX) = ''
DECLARE #COlsAlias NVARCHAR(MAX) = ''
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Group NVARCHAR(50) = 'Group1'
SELECT
#COlsID = #ColsID + ',' + z.TagDescID,
#COlsAlias = #COlsAlias + ',' + z.TagDescID + ' AS ' + z.ReportTag
FROM
(SELECT DISTINCT TOP 50 tblDataLog.TagDescID ID, QUOTENAME(CONVERT(NVARCHAR(5), tblDataLog.TagDescID )) TagDescID, QUOTENAME(tblTagDescription.ReportTag) ReportTag
FROM tblDataLog
INNER JOIN tblTagDescription ON tblDataLog.TagDescID = tblTagDescription.ID
ORDER BY tblDataLog.TagDescID
) z
SET #COlsID= STUFF(#COlsID,1,1,'')
SET #COlsAlias= STUFF(#COlsAlias,1,1,'')
SET #SQL='select [DATE],SHIFT, ' + #COlsAlias + ' into ##MYTABLE from ( select [Date], Value, TagDescID,
(CASE
WHEN ((DATEPART(hour,[DATE]))>6 and (DATEPART(hour,[DATE]))<14) THEN ''A''
WHEN ((DATEPART(hour,[DATE]))>=14 and (DATEPART(hour,[DATE]))<22) THEN ''B''
WHEN ((DATEPART(hour,[DATE]))>=22 or (DATEPART(hour,[DATE]))<6) THEN ''C''
END )AS SHIFT
from tblDataLog )d pivot(max(Value) for TagDescID in (' + #COlsID + ')) piv;'
EXEC (#SQL)

how to select Columns using SQL which has data length greater than given length

I have a table with specific columns and rows. I would like to select columns which has data more than length 7.
For ex:
Table has columns
Name Address PhoneNumber
AAA AAAAAAAA 12345678
BBBBB BBBBBBB 47854
CCC FFFF 76643
Here columns 'Address ' and 'Phone Number' has data length more than 7. So it should display,
Address
PhoneNumber
as results. This is for a particular table. Here I do not know already that Address and PhoneNumber are the columns which have data greater than length 7. Only from the query result I will be able to find it.
SELECT <<all_columns>> from table where length(columns)>7 is my input requirement.
The LENGTH or LEN functions in 'Where' clause gives option to give only one specific column name
instead of LENGTH(COL_NAME) , I need option as where LENGTH(<> or something like LENGTH(*)) > 7 should be given as input.
How that can be achieved?
So HAVING is probably the clause youd want to use. Obviously, you can expand to include all columns and increase the having. see this:
SELECT
Name,
Address,
Phonenumber,
LEN(Address) AS AddyLength
FROM
yourTables
GROUP BY
Name,
Address,
Phonenumber,
HAVING
LEN(Address)>7
If you can live with the results in columns rather than rows:
select (case when max(length(name)) > 7 then 'Name;' else '' end) ||
(case when max(length(address)) > 7 then 'address;' else '' end) ||
(case when max(length(phone)) > 7 then 'phone;' else '' end)
from t;
As I read you need a dynamic sql for larger tables than your example (that should be part of your question)
I used unpivot to compare all lengths at once
DECLARE #TableName VARCHAR(100) = 'YourTableName'
DECLARE #MaxLen INT = 7
DECLARE #Definition
TABLE (
ColumnName VARCHAR(50)
)
INSERT #Definition
SELECT C.Name
FROM
sys.columns C
JOIN sys.tables T
ON C.object_id = T.object_id
WHERE t.name = #TableName
DECLARE #Columns VARCHAR(MAX) = ''
DECLARE #ColumnsWithCast VARCHAR(MAX) = ''
SET #Columns = STUFF(
(SELECT ',' + ColumnName
FROM #Definition
FOR XML PATH('')
),
1,
1,
'')
SET #ColumnsWithCast = STUFF(
(SELECT ',CAST(' + ColumnName + ' AS VARCHAR(MAX)) AS ' + ColumnName
FROM #Definition
FOR XML PATH('')
),
1,
1,
'')
DECLARE #SQL NVARCHAR(MAX) = N'
SELECT DISTINCT
Field
FROM (
SELECT
' + #ColumnsWithCast + '
FROM ' + #TableName + ' A
) p
UNPIVOT (
Value FOR Field IN (
' + #Columns + '
)
)AS unpvt
WHERE LEN(Value) > #MaxLen
'
DECLARE #ParamDefinition NVARCHAR(100) = N'#MaxLen INT'
EXEC sp_executesql #SQL, #ParamDefinition, #MaxLen = #MaxLen
It will generate this code with all the existing columns
SELECT DISTINCT
Field
FROM (
SELECT
CAST(Name AS VARCHAR(MAX)) AS Name,
CAST(Address AS VARCHAR(MAX)) AS Address,
CAST(PhoneNumber AS VARCHAR(MAX)) AS PhoneNumber,
FROM HIERARCHY A
) p
UNPIVOT (
Value FOR Field IN (
Name, Address, PhoneNumber
)
)AS unpvt
WHERE LEN(Value) > #MaxLen

Dynamic SQL query with repeating elements

I am trying to build a query using dynamic sql which looks like following,
'select dense_rank () over(partition by column1 order by column1),
dense_rank () over(partition by column1,column2 order by column2),
dense_rank () over(partition by column1,column2,column3 order column3) from tablename'
I have used STUFF to build the query but able to create query only with one columns and could not repeat columns i.e.'partition by column1, column2'.
How to achieve this using STUFF or is there other way to do it ?
You can do it with two queries that concatenates string. One outer that builds the column list and one inner that builds the column list for the partition by clause.
Sample table:
create table T(C1 int, C2 int, C3 int);
Code:
declare #SQL nvarchar(max);
with C as
(
select C.name,
-- Use order by to control order of columns
row_number() over(order by C.column_id) as rn
from sys.columns as C
where object_id = object_id('T') -- Specify the name of the table
-- Optionally filter out any columns that
-- should not be included
)
select #SQL = 'select '+
stuff((
select ',dense_rank() over(partition by '+
stuff((
select ','+c2.name
from C as C2
where C2.rn <= C.rn
order by C2.rn
for xml path(''), type
).value('text()[1]', 'nvarchar(max)'), 1, 1, '') +
' order by '+C.name+')'
from C
for xml path(''), type
).value('text()[1]', 'nvarchar(max)'), 1, 1,'')+
' from T';
print #SQL;
--exec (#SQL);
Result:
select dense_rank() over(partition by C3 order by C3),
dense_rank() over(partition by C3,C2 order by C2),
dense_rank() over(partition by C3,C2,C1 order by C1)
from T
Note: STUFF (Transact-SQL) does not concatenate strings. It is used to insert one string into another string. In the code above, stuff is used to remove the leading comma in the concatenated string by inserting an empty string to the first position and overwriting 1 character. The actual concatenation is done by for xml.
Declare #col_name varchar(100)
Declare #colnm varchar(max)
Declare #multicol int
Declare #sqlstr varchar(max)
Declare Cur_1 cursor
for
Select name
from sys.columns
where object_id=object_id('tablename')
order by name
OPEN Cur_1
FETCH NEXT FROM Cur_1
INTO #col_name
set #colnm=''
set #multicol=0
WHILE ##FETCH_STATUS = 0
BEGIN
if #multicol=1
begin
set #colnm=#colnm + ',' + #col_name
end
else
begin
set #colnm= #colnm+ #col_name
end
if #multicol=0
begin
set #sqlstr='Select dense_rank () over(partition by ' + #colnm + ' order by ' + #colnm + '),'
end
else
begin
set #sqlstr=#sqlstr + char(13) + char(10) + 'dense_rank () over(partition by ' + #colnm + ' order by ' + #colnm + '),'
end
FETCH NEXT FROM Cur_1
INTO #col_name
set #multicol=1
END
CLOSE Cur_1;
DEALLOCATE Cur_1;
set #sqlstr=left(#sqlstr,len(#sqlstr)-1) + + char(13) + char(10) + 'from tablename'
print #sqlstr
Declare #QueryString Varchar(Max)
Declare #TblNm Varchar(100)
Set #TblNm='[dbo].[SampleTable]'
Set #QueryString=
(
Select ',dense_rank () over(partition by '+ ColNm + ' Order by [' + name + '])' + char(10) AS [text()]
From
(
SELECT name,ColNm = STUFF(
(
SELECT ',[' + name + ']'
FROM sys.columns
where object_id=object_id(#TblNm) and column_id<=Tbl1.column_id
FOR XML PATH ('')
), 1, 1, ''
)
FROM sys.columns Tbl1
where object_id=object_id(#TblNm)
) ColDtl
For XML PATH ('')
)
Set #QueryString='Select '
+ substring(#QueryString,2,len(#QueryString))
+ ' from ' + #TblNm
Select #QueryString as ExecString
--Exec (#QueryString)

How to provide custom name to column in pivoting

I have a table like this:
id unit
1 mm
2 cm
3 kg
When I perform pivot operation on this, I am getting result as follows:
1 2 3
mm cm kg
Is it possible to get custom column names here, something like this:
d1 d2 d3
mm cm kg
I am using Pivot for this as:
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE table #t
(id varchar(max),unit varchar(max))
insert into #t (id,unit)values
(1,'kg'),
(2,'cm'),
(3,'mm'),
(4,'m')
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ',', '') + N'[' + cast(tbl.id as varchar(max)) + ']'
FROM (
SELECT DISTINCT id
FROM #t
) AS tbl
SELECT #statement = 'select *
INTO ##temp
from (
SELECT id,[unit]
FROM #t
) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
SELECT * FROM ##temp
DROP TABLE #t
DROP TABLE ##temp
Is it possible?
Thanks
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE TABLE #t (
id VARCHAR(10),
unit VARCHAR(100)
)
INSERT INTO #t (id, unit)
VALUES
('1', 'kg'),
('2', 'cm'),
('3', 'mm'),
('4', 'mm')
DECLARE #SQL NVARCHAR(MAX), #columns NVARCHAR(MAX)
SELECT #columns = STUFF((
SELECT ',[D' + id + ']'
FROM #t
FOR XML PATH('')), 1, 1, '')
SELECT #SQL = '
SELECT *
FROM (
SELECT [unit], col = N''D'' + id
FROM #t
) s
PIVOT (MAX(unit) FOR col IN (' + #columns + ')) p'
EXEC sys.sp_executesql #SQL
Just add a prefix to your ID. Example
SELECT #statement = 'select * INTO ##temp from
( SELECT [id] = ''d''+id,[unit] FROM #t ) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt '
Also it's terrible practice to use global temp tables! Especially one named ##temp
You can use a CASE expression with a dynamic sql query.
CREATE TABLE #t
(
id INT,
unit VARCHAR(2)
);
INSERT INTO #t VALUES
(1,'mm'),
(2,'cm'),
(3,'kg');
DECLARE #query AS VARCHAR(MAX);
SELECT #query = 'SELECT ' +
STUFF
(
(
SELECT DISTINCT ',MAX(CASE WHEN id = '+ CAST(id AS VARCHAR(10))
+ ' THEN unit END) AS d' + CAST(id AS VARCHAR(10))
FROM #t
FOR XML PATH('')
),
1,1,'');
SELECT #query += ' FROM #t;';
EXECUTE(#query);
Result
+----+----+----+
| d1 | d2 | d3 |
+----+----+----+
| mm | cm | kg |
+----+----+----+
SELECT #statement = 'select * INTO ##temp from ( SELECT ''d''+id AS [id],[unit] FROM #t ) as s PIVOT (max(unit) FOR id in(' + #columns + ')) as pvt '