I have a table say Configs in which I have the column names
id | Name
-------------
1 | Fruits
2 | Vegetables
3 | Pulses
I have another table say Details
id |Field1 | Field2| Field3 | Active | Mandatory
-------------------------------------------------
1 |Apple |Potato |Red gram | 1 |0
2 |Mango |Peas |Chick Peas| 0 |0
I need field1, field2, field3 to be selected as the name of 1st table
eg.
select
id,
Field1 as Fruits,
Field2 as Vegetables,
Field3 as pulses,
Active,
Mandatory
From
Details
How do I do it?
declare #sql nvarchar(max)
select #sql = isnull(#sql + ',' ,'')
+ N'Field' + convert(varchar(10), id) + ' as ' + quotename(Name)
from Config
-- Form the dynamic SQL
select #sql = 'SELECT id,'
+ #sql
+ ',Active, Mandatory '
+ 'FROM Details'
-- Print to verify
print #sql
-- Execute it
exec sp_executesql #sql
Something like these perhaps:
Method #1
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Configs(ID INT, Name NVARCHAR(25));
ELSE
TRUNCATE TABLE Configs
INSERT INTO Configs VALUES(1, 'Fruits'), (2, 'Vegetables'), (3, 'Pulses');
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Details(ID INT, Field1 NVARCHAR(25), Field2 NVARCHAR(25), Field3 NVARCHAR(25)
,Active BIT, Mandatory BIT);
ELSE
TRUNCATE TABLE Details
INSERT INTO Details VALUES(1, 'Apples', 'Potato', 'Red Gram', 1, 0)
,(2, 'Mango', 'Peas', 'Chick Peas', 0, 0);
DECLARE #Sql NVARCHAR(MAX);
SELECT #Sql = 'SELECT id,' + CHAR(13)
+ ' [Field1] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 1)) +',' + CHAR(13)
+ ' [Field2] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 2)) +',' + CHAR(13)
+ ' [Field3] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 3)) +',' + CHAR(13)
+ ' Active,' + CHAR(13)
+ ' Mandatory ' + CHAR(13)
+ 'From Details' + CHAR(13);
PRINT #Sql;
EXEC sp_executesql #Sql;
Method #2
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Configs(ID INT, Name NVARCHAR(25));
ELSE
TRUNCATE TABLE Configs
INSERT INTO Configs VALUES(1, 'Fruits'), (2, 'Vegetables'), (3, 'Pulses');
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Details(ID INT, Field1 NVARCHAR(25), Field2 NVARCHAR(25), Field3 NVARCHAR(25)
,Active BIT, Mandatory BIT);
ELSE
TRUNCATE TABLE Details
INSERT INTO Details VALUES(1, 'Apples', 'Potato', 'Red Gram', 1, 0)
,(2, 'Mango', 'Peas', 'Chick Peas', 0, 0);
DECLARE #Sql NVARCHAR(MAX) = 'SELECT' + CHAR(13) + CHAR(9) + 'ID' + CHAR(13);
SELECT
#Sql += STUFF((
SELECT
CHAR(9) + ',' + QUOTENAME('Field' + LTRIM(STR(ROW_NUMBER()OVER(ORDER BY ID)))) + ' AS ' + QUOTENAME(Name) + CHAR(13)
FROM
Configs
FOR XML PATH(''), type).value('.', 'nvarchar(max)'), 1, 0, '');
SELECT
#Sql += CHAR(9) +',Active' + CHAR(13)
+ CHAR(9) +',Mandatory ' + CHAR(13)
+ 'From Details' + CHAR(13);
PRINT #Sql;
EXEC sp_executesql #Sql;
Related
I have built a stored procedure that aims to identify duplicates in a table and to display the duplicated rows in a meaningful order. It looks like this:
CREATE PROCEDURE [dbo].[spFindDuplicates]
#tableName nvarchar(255),
#field1 nvarchar(255),
#field2 nvarchar(255) = '1',
#field3 nvarchar(255) = '2',
#field4 nvarchar(255) = '3',
#field5 nvarchar(255) = '4'
AS
BEGIN
DECLARE #query AS nvarchar(MAX);
SET #query = '
SELECT *
FROM ' + #tableName + '
WHERE CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
IN
(
SELECT CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
FROM ' + #tableName + '
GROUP BY CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
HAVING COUNT(*) > 1
)
ORDER BY ' + #field1 + ', ' + #field2 + ', ' + #field3 + ', ' + #field4 + ', ' + #field5
EXECUTE(#query);
END
GO
--Example:
EXEC spFindDuplicates #tableName = 'someRandomTable', #field1 = 'firstField', #field2 = 'secondField', #field3 = 'thirdField'
As you can see, I can use at most 5 different fields that I concatenate in order for me to get a key used to determine whether we have a duplicate or not. Please note that I use the CAST function to be able to concatenate fields with various datatypes (varchar, int, dates, etc.).
When I execute the above stored procedure with 5 different fields, it works fine. But I would like to be able to run it with a variable number of fields (from 1 to 5), which is why I provided default values for #field2 to #field5.
But when I execute it with the above example (3 fields provided), I get the following error message:
A column has been specified more than once in the order by list. Columns in the order by list must be unique.
QUESTION: How can I keep ordering the resulting table without getting an error?
BONUS QUESTION: If you find a dynamic way to use that stored procedure with any number of fields (4, 17, or whatever), that'd be even more useful to me.
Like I said in the comments, injection is a huge problem here, and you need to consider it. Saying "Let's consider I don't mind about injection" is naïve and you need to change that attitude. Always make your SQL safe; then there are no excuses and chances for your application being compromised.
As what you are after, I suspect this achieves the goal. There's no need for the subquery to scan your table with an IN here, you can make use of COUNT and the OVER clause within a CTE.
CREATE PROCEDURE [dbo].[FindDuplicates] --I've removed te sp prefix, as sp_ is reserved by MS
#tableName sysname,
#field1 sysname,
#field2 sysname = NULL,
#field3 sysname = NULL,
#field4 sysname = NULL,
#field5 sysname = NULL
AS BEGIN
DECLARE #query AS nvarchar(MAX);
SET #query = N'WITH CTE AS(' + NCHAR(10) +
N' SELECT *' + NCHAR(10) +
N' COUNT(*) OVER (PARTITION BY ' + STUFF(CONCAT(N',' + QUOTENAME(#field1),N',' + QUOTENAME(#field2),N',' + QUOTENAME(#field3),N',' + QUOTENAME(#field4),N',' + QUOTENAME(#field5)),1,1,N'') + N' AS RowCount' + NCHAR(10) +
N' FROM ' + QUOTENAME(#tableName) + N')' + NCHAR(10) +
N'SELECT *' + NCHAR(10) +
N'FROM CTE' + NCHAR(10) +
N'WHERE RowCount > 1' + NCHAR(10) +
N'ORDER BY ' + STUFF(CONCAT(N',' + QUOTENAME(#field1),N',' + QUOTENAME(#field2),N',' + QUOTENAME(#field3),N',' + QUOTENAME(#field4),N',' + QUOTENAME(#field5)),1,1,N'') + N';';
PRINT #query;
--EXEC sys.sp_executesql #query; --Uncomment to rrun the actual query
END
GO
For the command you gave us EXEC dbo.FindDuplicates #tableName = 'someRandomTable', #field1 = 'firstField', #field2 = 'secondField', #field3 = 'thirdField';, this returns the SQL:
WITH CTE AS(
SELECT *
COUNT(*) OVER (PARTITION BY [firstField],[secondField],[thirdField] AS RowCount
FROM [someRandomTable])
SELECT *
FROM CTE
WHERE RowCount > 1
ORDER BY [firstField],[secondField],[thirdField];
Which, I believe gives you the behaviour you are after.
Edited the code to check if the column list exists on the sys.columns there by making sure we get only the columns which are appropriate.
CREATE FUNCTION dbo.fn_SplitString
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
ALTER PROCEDURE [dbo].[spFindDuplicates]
#tableName nvarchar(255),
#columnlist nvarchar(max)
AS
BEGIN
DECLARE #query AS nvarchar(MAX);
SET #columnlist = (SELECT STUFF((SELECT ','+'['+[name]+']'
FROM SYS.columns
WHERE object_id = object_id(#tableName)
AND [Name] IN
(
SELECT Item
FROM dbo.fn_SplitString(#columnlist,',')
)
FOR XML PATH('')
)
,1,1,''))
PRINT #columnlist
SET #query = 'SELECT * FROM (SELECT '+CAST(#columnlist AS NVARCHAR(MAX))+'
FROM '+CAST(#tableName AS nvarchar(MAX))+'
GROUP BY '+CAST(#columnlist AS NVARCHAR(MAX))+'
HAVING COUNT(*) > 1)Res1
ORDER BY '+#columnlist
EXEC SP_EXECUTESQL #query;
END
GO
I have a Input table
Header DisplayName
Field1 EmpName
Field2 EmpID
Field3 IsActive
Filed4 Dept
And a Output table:
Field1 Field2 Field3 Filed4
A 1 + Q
B 2 + R
How can I map the Display names from Input table onto the output table without explicitly specifying "Select Field1 AS 'EmpName', Field2 AS 'EmpID'" and so on?
Is there a fancy way of mapping the output table in a way that each field picks the Display Name from Input table?
TIA
Not sure about your situation, but I would first try to handle with a view over your output table first. However, if that is now possible, you can use dynamic SQL:
DECLARE #Sql NVARCHAR(MAX) = 'SELECT '
+ STUFF((
SELECT ', [' + Header + '] AS [' + DisplayName + ']' AS [text()]
FROM tblInput FOR XML PATH('')), 1, 2, '')
+ ' FROM tblOutput'
EXEC sys.sp_executesql #Sql
It will support variables injected into SQL if you need as well.
DECLARE #Sql NVARCHAR(MAX) = 'SELECT '
+ STUFF((
SELECT ', [' + Header + '] AS [' + DisplayName + ']' AS [text()]
FROM tblInput FOR XML PATH('')), 1, 2, '')
+ ' FROM tblOutput O WHERE Field1 = #Field1'
EXEC sys.sp_executesql #Sql, N'#Field1 VARCHAR(50)', #Field1 = 'A'
I have a procedure where I pass temporary table name as parameter. For each dataset inside I need to get rowcount. How to achieve this?
I need something like:
CREATE PROCEDURE sp_processing
#temp_table_name varchar(50)
AS
DECLARE #log varchar(max)
/* get list of keys inside temp_table_name */
/* just a pseudo-code example */
SET #l_cursor = CURSOR FOR
SELECT Key1, Key2, Key3, count(*)
FROM #temp_table_name -- table name passed as text
GROUP by Key1, Key2, Key3;
WHILE "there are results"
BEGIN
#log += #Key1 +', '+ #Key2 +', '+ #Key3 +', '+ #count + CHAR(13)+CHAR(10);
END
UPDATE log_table SET log_column = #log WHERE....;
END /* procedure */
Is there a way to loop this?
I know I have option to fetch results to a table type and THEN loop, but that requires to use a table type, so wondering if this is achievable without a table variable.
EDIT: I need just need to print count for each set of keys.
This worked for me:
DECLARE #l_sql nvarchar(max)
DECLARE #temp_table_name varchar(50) = 'SOME_TABLE'
DECLARE #combinedString varchar(max)
SET #l_sql = 'SELECT #combinedString = COALESCE(#combinedString, '''') + convert(varchar,[Key1]) +'', ''+ convert(varchar,[Key3]) +'': ''+ convert(varchar,COUNT(*)) + ''| '' + CHAR(13)+CHAR(10) '
+ ' FROM ' + #temp_table_name
+ ' GROUP BY [Key1], [Key3]'
+ ' ORDER BY [Key1], [Key3]';
EXECUTE sp_executesql #l_sql, N'#combinedString varchar(max) OUTPUT', #combinedString = #combinedString OUTPUT ;
SELECT #combinedString
Result:
1, 1: 4|
1, 2: 2|
1, 3: 1|
2, 5: 1|
You should always try to avoid looping and cursor. This is a set based solution for your case. Please review (specially the update filter) and see if it suits your needs.
CREATE PROCEDURE sp_processing
#temp_table_name varchar(50)
AS
BEGIN
DECLARE #DynamicSQL VARCHAR(MAX) = '
;WITH LogRecords AS
(
SELECT
LogRecord =
ISNULL(T.Key1, '''') + '','' +
ISNULL(T.Key2, '''') + '','' +
ISNULL(T.Key2, '''') + '','' +
CONVERT(VARCHAR(20), COUNT(1))
FROM
QUOTENAME(''' + #temp_table_name + ''') AS T
GROUP BY
T.Key1,
T.Key2,
T.Key3
)
UPDATE L SET
log_column = STUFF(
(
SELECT
R.LogRecord + CHAR(13) + CHAR(10)
FROM
LogRecords AS R
FOR XML
PATH('')
),
1, 1, '')
FROM
log_table AS L
WHERE
L.IdFilter = 999999999'
PRINT #DynamicSQL
-- EXEC (#DynamicSQL)
END
I'm looking for an efficient way to convert rows to columns in SQL server, I heard that PIVOT is not very fast, and I need to deal with lot of records.
This is my example:
-------------------------------
| Id | Value | ColumnName |
-------------------------------
| 1 | John | FirstName |
| 2 | 2.4 | Amount |
| 3 | ZH1E4A | PostalCode |
| 4 | Fork | LastName |
| 5 | 857685 | AccountNumber |
-------------------------------
This is my result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 2.4 | ZH1E4A | Fork | 857685 |
---------------------------------------------------------------------
How can I build the result?
There are several ways that you can transform data from multiple rows into columns.
Using PIVOT
In SQL Server you can use the PIVOT function to transform the data from rows to columns:
select Firstname, Amount, PostalCode, LastName, AccountNumber
from
(
select value, columnname
from yourtable
) d
pivot
(
max(value)
for columnname in (Firstname, Amount, PostalCode, LastName, AccountNumber)
) piv;
See Demo.
Pivot with unknown number of columnnames
If you have an unknown number of columnnames that you want to transpose, then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from yourtable
group by ColumnName, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName
from yourtable
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
See Demo.
Using an aggregate function
If you do not want to use the PIVOT function, then you can use an aggregate function with a CASE expression:
select
max(case when columnname = 'FirstName' then value end) Firstname,
max(case when columnname = 'Amount' then value end) Amount,
max(case when columnname = 'PostalCode' then value end) PostalCode,
max(case when columnname = 'LastName' then value end) LastName,
max(case when columnname = 'AccountNumber' then value end) AccountNumber
from yourtable
See Demo.
Using multiple joins
This could also be completed using multiple joins, but you will need some column to associate each of the rows which you do not have in your sample data. But the basic syntax would be:
select fn.value as FirstName,
a.value as Amount,
pc.value as PostalCode,
ln.value as LastName,
an.value as AccountNumber
from yourtable fn
left join yourtable a
on fn.somecol = a.somecol
and a.columnname = 'Amount'
left join yourtable pc
on fn.somecol = pc.somecol
and pc.columnname = 'PostalCode'
left join yourtable ln
on fn.somecol = ln.somecol
and ln.columnname = 'LastName'
left join yourtable an
on fn.somecol = an.somecol
and an.columnname = 'AccountNumber'
where fn.columnname = 'Firstname'
This is rather a method than just a single script but gives you much more flexibility.
First of all There are 3 objects:
User defined TABLE type [ColumnActionList] -> holds data as
parameter
SP [proc_PivotPrepare] -> prepares our data
SP [proc_PivotExecute] -> execute the script
CREATE TYPE [dbo].[ColumnActionList] AS TABLE
(
[ID] [smallint] NOT NULL,
[ColumnName] nvarchar NOT NULL,
[Action] nchar NOT NULL
);
GO
CREATE PROCEDURE [dbo].[proc_PivotPrepare]
(
#DB_Name nvarchar(128),
#TableName nvarchar(128)
)
AS
SELECT #DB_Name = ISNULL(#DB_Name,db_name())
DECLARE #SQL_Code nvarchar(max)
DECLARE #MyTab TABLE (ID smallint identity(1,1), [Column_Name] nvarchar(128), [Type] nchar(1), [Set Action SQL] nvarchar(max));
SELECT #SQL_Code = 'SELECT [<| SQL_Code |>] = '' '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Declare user defined type [ID] / [ColumnName] / [PivotAction] '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''DECLARE #ColumnListWithActions ColumnActionList;'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Set [PivotAction] (''''S'''' as default) to select dimentions and values '' '
+ 'UNION ALL '
+ 'SELECT ''-----|'''
+ 'UNION ALL '
+ 'SELECT ''-----| ''''S'''' = Stable column || ''''D'''' = Dimention column || ''''V'''' = Value column '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''INSERT INTO #ColumnListWithActions VALUES ('' + CAST( ROW_NUMBER() OVER (ORDER BY [NAME]) as nvarchar(10)) + '', '' + '''''''' + [NAME] + ''''''''+ '', ''''S'''');'''
+ 'FROM [' + #DB_Name + '].sys.columns '
+ 'WHERE object_id = object_id(''[' + #DB_Name + ']..[' + #TableName + ']'') '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Execute sp_PivotExecute with parameters: columns and dimentions and main table name'' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''EXEC [dbo].[sp_PivotExecute] #ColumnListWithActions, ' + '''''' + #TableName + '''''' + ';'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
EXECUTE SP_EXECUTESQL #SQL_Code;
GO
CREATE PROCEDURE [dbo].[sp_PivotExecute]
(
#ColumnListWithActions ColumnActionList ReadOnly
,#TableName nvarchar(128)
)
AS
--#######################################################################################################################
--###| Step 1 - Select our user-defined-table-variable into temp table
--#######################################################################################################################
IF OBJECT_ID('tempdb.dbo.#ColumnListWithActions', 'U') IS NOT NULL DROP TABLE #ColumnListWithActions;
SELECT * INTO #ColumnListWithActions FROM #ColumnListWithActions;
--#######################################################################################################################
--###| Step 2 - Preparing lists of column groups as strings:
--#######################################################################################################################
DECLARE #ColumnName nvarchar(128)
DECLARE #Destiny nchar(1)
DECLARE #ListOfColumns_Stable nvarchar(max)
DECLARE #ListOfColumns_Dimension nvarchar(max)
DECLARE #ListOfColumns_Variable nvarchar(max)
--############################
--###| Cursor for List of Stable Columns
--############################
DECLARE ColumnListStringCreator_S CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'S'
OPEN ColumnListStringCreator_S;
FETCH NEXT FROM ColumnListStringCreator_S
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Stable = ISNULL(#ListOfColumns_Stable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_S INTO #ColumnName
END
CLOSE ColumnListStringCreator_S;
DEALLOCATE ColumnListStringCreator_S;
--############################
--###| Cursor for List of Dimension Columns
--############################
DECLARE ColumnListStringCreator_D CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'D'
OPEN ColumnListStringCreator_D;
FETCH NEXT FROM ColumnListStringCreator_D
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Dimension = ISNULL(#ListOfColumns_Dimension, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_D INTO #ColumnName
END
CLOSE ColumnListStringCreator_D;
DEALLOCATE ColumnListStringCreator_D;
--############################
--###| Cursor for List of Variable Columns
--############################
DECLARE ColumnListStringCreator_V CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'V'
OPEN ColumnListStringCreator_V;
FETCH NEXT FROM ColumnListStringCreator_V
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Variable = ISNULL(#ListOfColumns_Variable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_V INTO #ColumnName
END
CLOSE ColumnListStringCreator_V;
DEALLOCATE ColumnListStringCreator_V;
SELECT #ListOfColumns_Variable = LEFT(#ListOfColumns_Variable, LEN(#ListOfColumns_Variable) - 1);
SELECT #ListOfColumns_Dimension = LEFT(#ListOfColumns_Dimension, LEN(#ListOfColumns_Dimension) - 1);
SELECT #ListOfColumns_Stable = LEFT(#ListOfColumns_Stable, LEN(#ListOfColumns_Stable) - 1);
--#######################################################################################################################
--###| Step 3 - Preparing table with all possible connections between Dimension columns excluding NULLs
--#######################################################################################################################
DECLARE #DIM_TAB TABLE ([DIM_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #DIM_TAB
SELECT [DIM_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'D';
DECLARE #DIM_ID smallint;
SELECT #DIM_ID = 1;
DECLARE #SQL_Dimentions nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_Dimentions', 'U') IS NOT NULL DROP TABLE ##ALL_Dimentions;
SELECT #SQL_Dimentions = 'SELECT [xxx_ID_xxx] = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Dimension + '), ' + #ListOfColumns_Dimension
+ ' INTO ##ALL_Dimentions '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Dimension + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
WHILE #DIM_ID <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #SQL_Dimentions = #SQL_Dimentions + 'AND ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
END
SELECT #SQL_Dimentions = #SQL_Dimentions + ' )x';
EXECUTE SP_EXECUTESQL #SQL_Dimentions;
--#######################################################################################################################
--###| Step 4 - Preparing table with all possible connections between Stable columns excluding NULLs
--#######################################################################################################################
DECLARE #StabPos_TAB TABLE ([StabPos_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #StabPos_TAB
SELECT [StabPos_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #StabPos_ID smallint;
SELECT #StabPos_ID = 1;
DECLARE #SQL_MainStableColumnTable nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_StableColumns', 'U') IS NOT NULL DROP TABLE ##ALL_StableColumns;
SELECT #SQL_MainStableColumnTable = 'SELECT xxx_ID_xxx = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Stable + '), ' + #ListOfColumns_Stable
+ ' INTO ##ALL_StableColumns '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Stable + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
WHILE #StabPos_ID <= (SELECT MAX([StabPos_ID]) FROM #StabPos_TAB)
BEGIN
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + 'AND ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
END
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + ' )x';
EXECUTE SP_EXECUTESQL #SQL_MainStableColumnTable;
--#######################################################################################################################
--###| Step 5 - Preparing table with all options ID
--#######################################################################################################################
DECLARE #FULL_SQL_1 NVARCHAR(MAX)
SELECT #FULL_SQL_1 = ''
DECLARE #i smallint
IF OBJECT_ID('tempdb.dbo.##FinalTab', 'U') IS NOT NULL DROP TABLE ##FinalTab;
SELECT #FULL_SQL_1 = 'SELECT t.*, dim.[xxx_ID_xxx] '
+ ' INTO ##FinalTab '
+ 'FROM ' + #TableName + ' t '
+ 'JOIN ##ALL_Dimentions dim '
+ 'ON t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1);
SELECT #i = 2
WHILE #i <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #FULL_SQL_1 = #FULL_SQL_1 + ' AND t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i)
SELECT #i = #i +1
END
EXECUTE SP_EXECUTESQL #FULL_SQL_1
--#######################################################################################################################
--###| Step 6 - Selecting final data
--#######################################################################################################################
DECLARE #STAB_TAB TABLE ([STAB_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #STAB_TAB
SELECT [STAB_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #VAR_TAB TABLE ([VAR_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #VAR_TAB
SELECT [VAR_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'V';
DECLARE #y smallint;
DECLARE #x smallint;
DECLARE #z smallint;
DECLARE #FinalCode nvarchar(max)
SELECT #FinalCode = ' SELECT ID1.*'
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #z = 1
WHILE #z <= (SELECT MAX([VAR_ID]) FROM #VAR_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ', [ID' + CAST((#y) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z) + '] = ID' + CAST((#y + 1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z)
SELECT #z = #z + 1
END
SELECT #y = #y + 1
END
SELECT #FinalCode = #FinalCode +
' FROM ( SELECT * FROM ##ALL_StableColumns)ID1';
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #x = 1
SELECT #FinalCode = #FinalCode
+ ' LEFT JOIN (SELECT ' + #ListOfColumns_Stable + ' , ' + #ListOfColumns_Variable
+ ' FROM ##FinalTab WHERE [xxx_ID_xxx] = '
+ CAST(#y as varchar(10)) + ' )ID' + CAST((#y + 1) as varchar(10))
+ ' ON 1 = 1'
WHILE #x <= (SELECT MAX([STAB_ID]) FROM #STAB_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ' AND ID1.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x) + ' = ID' + CAST((#y+1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x)
SELECT #x = #x +1
END
SELECT #y = #y + 1
END
SELECT * FROM ##ALL_Dimentions;
EXECUTE SP_EXECUTESQL #FinalCode;
From executing the first query (by passing source DB and table name) you will get a pre-created execution query for the second SP, all you have to do is define is the column from your source:
+ Stable
+ Value (will be used to concentrate values based on that)
+ Dim (column you want to use to pivot by)
Names and datatypes will be defined automatically!
I cant recommend it for any production environments but does the job for adhoc BI requests.
I modified Taryn's answer ("Pivot with unknown number of columnnames" version) to show more than 1 row in the result. This requires to have an additional "Group" column
DROP TABLE #yourtable
CREATE table #yourtable
([Id] int,[Group] int, [Value] varchar(6), [ColumnName] varchar(13))
;
INSERT INTO #yourtable
([Id],[Group], [Value], [ColumnName])
VALUES
(1,1, 'John', 'FirstName'),
(2,1, '2.4', 'Amount'),
(3,1, 'ZH1E4A', 'PostalCode'),
(4,1, 'Fork', 'LastName'),
(5,1, '857685', 'AccountNumber'),
(6,2, 'Pedro', 'FirstName'),
(7,2, '5.1', 'Amount'),
(8,2, '123456', 'PostalCode'),
(9,2, 'Torres', 'LastName'),
(10,2, '857686', 'AccountNumber')
;
;
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from #yourtable
group by [Group], ColumnName, id
having [group] = (SELECT TOP 1 MIN([Group])FROM #yourtable)
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName,[Group]
from #yourtable
GROUP BY [Group],ColumnName,Value
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
Please try
CREATE TABLE pvt (Present int, [Absent] int);
GO
INSERT INTO pvt VALUES (10,40);
GO
--Unpivot the table.
SELECT Code, Value
FROM
(SELECT Present, Absent
FROM pvt) p
UNPIVOT
(Value FOR Code IN
(Present, [Absent])
)AS unpvt;
GO
DROP TABLE pvt
One more option which could be very useful is using CROSS APPLY
-- Original data
SELECT * FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
-- row to columns using CROSS APPLY
SELECT Stage.id,v.idd, v.colc
FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
CROSS APPLY (VALUES ('col1', col1),('col2', col2),('col3', col3)) AS v(idd,colc)
GO
I am using SQL Server 2008. I use to take the script of my data from SQL table using Tasks --> Generate Scripts option.
Here is my problem:
Let's say I have 21,000 records in Employee table. When I take the script of this table, it takes the insert script for all 21000 records. What is the solution if I want to take only the script of 18000 records from the table?
Is there any solution using SQL query or from the tasks wizard?
Thanks in advance...
Create a new View where you select your desired rows from your Employee table e.g. SELECT TOP 21000...
Then simply script that View instead of the Table.
In case the views are not an option for you I wrote the following code based on the Aaron Bertrand's answer here that will give the insert statement for a single record in the db.
CREATE PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value NVARCHAR(10) -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + '','' + ' + 'ISNULL('+REPLICATE(CHAR(39),4)+'+RTRIM(' +
CASE WHEN system_type_id IN (40,41,42,43,58,61) -- dateteime and time stamp type
THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
WHEN system_type_id IN (35) -- text type
THEN
'REPLACE(CAST(' + QUOTENAME(name) + 'as nvarchar(MAX)),'+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
ELSE
'REPLACE(' + QUOTENAME(name) + ','+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
END
+ ')+' + REPLICATE(CHAR(39),4) + ',''null'') + '
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39),2) + STUFF(#vals, 1, 6, '') + REPLICATE(CHAR(39),2) ;
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END
I took the above code and wrapped it the following proc that will use the where clause you give it to select which insert statements to create
CREATE PROCEDURE dbo.GenerateInserts
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#whereClause NVARCHAR(500) -- the where clause used to parse down the data
AS
BEGIN
declare #temp TABLE ( keyValue nvarchar(10), Pos int );
declare #result TABLE ( insertString nvarchar(MAX) );
declare #query NVARCHAR(MAX)
set #query =
'with qry as
(
SELECT ' + #pk_column + ' as KeyValue, ROW_NUMBER() over(ORDER BY ' + #pk_column + ') Pos
from ' + #table + '
' + #whereClause + '
)
select * from qry'
insert into #temp
exec sp_sqlexec #query
Declare #i int, #key nvarchar(10)
select #i = count(*) from #temp
WHILE #i > 0 BEGIN
select #key = KeyValue from #temp where Pos = #i
insert into #result
exec [dbo].[GenerateSingleInsert] #table, #pk_column, #key
set #i = #i - 1
END
select insertString from #result
END
Calling it could look like the following. You pass in the table name, the table primary key and the where clause and you should end up with your insert statements.
set #whereClause = 'where PrettyColorsId > 1000 and PrettyColorsID < 5000'
exec [dbo].GenerateInserts 'dbo.PrettyColors', 'PrettyColorsID', #whereClause
set #whereClause = 'where Color in (' + #SomeValues + ')'
exec [dbo].GenerateInserts 'dbo.PrettyColors', 'PrettyColorsID', #whereClause