How to prepend column names into column values? - sql

Having the following table:
| Some Table |
| Id | Name | Age |
| 23 | Marc | 41 |
| 54 | Edu | 34 |
I want to get:
|Another Table's Column| Id | Name | Age |
| ..... | Id#23 | Name#Marc | Age#41 |
| ..... | Id#54 | Name#Edu | Age#34 |
This query will be used inside a dynamic sql, the name of the table is going to have passed as a parameter.
The final query must show data of at least two tables, and only one of them must show data with his column names as a prefix.

Not sure I understand what you want, but here is the script to show column names within the result for any passed table:
DECLARE #t NVARCHAR(128) = '[Person].[Person]';
DECLARE #l NVARCHAR(2000) = '';
DECLARE #s NVARCHAR(4000) = '';
SELECT #l = (
SELECT ' ''#' + Name + ''' + CAST([' + Name + '] as VARCHAR(max)) as [' + Name + '],'
FROM sys.columns
WHERE object_id = OBJECT_ID(#t)
ORDER BY column_id
FOR XML PATH(''));
SELECT #s = 'SELECT ' + LEFT(#l,LEN(#l)-1) + ' FROM ' + #t + ';'
PRINT #s
EXEC (#s)
You can tune it to link another table or many tables

Related

Dynamic Statement to Get Top "n" for Each Individual Column from Ms SQL Table

Help me with this Dynamic statement perform faster, the statement will fetch top n values for each column from a table.
The table will have an "n" number of columns but will have a primary key. NULLs couldn't have been avoided as any other value is considered as VALID and should go to the database.
Table
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | 14.6 | 0 |
| 6110 | 72 | 14.1 | 1 |
| 6109 | 66 | 15.2 | NULL |
| 6108 | 68 | 14 | NULL |
| 6107 | 69 | 14 | NULL |
| 6106 | 61 | 14.8 | NULL |
| 6105 | 70 | NULL | NULL |
| 6104 | 64 | NULL | NULL |
| 6103 | 59 | NULL | NULL |
| 6102 | 49 | NULL | NULL |
+-------+------+------+------+
Result set,
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | NULL | 0 |
| 6110 | 72 | NULL | 1 |
| 6109 | NULL | 15.2 | NULL |
| 6106 | NULL | 14.8 | NULL |
+-------+------+------+------+
Dynamic SQL used to get current result set,
DECLARE #Columns VARCHAR(MAX); -- Param1
DECLARE #IdxColumn VARCHAR(250); --Param2
DECLARE #Limit VARCHAR(11); --Param3
DECLARE #SQL NVARCHAR(MAX)=''; --Param4
DECLARE #query NVARCHAR(MAX) = ' SELECT TOP (' + #pLimit + ') ' + #IdxColumn + ', ' + #Columns + ' FROM [Table] WHERE '
SET #SQL = #query + REPLACE(#Columns,',', ' IS NOT NULL ORDER BY '+ #IdxColumn + ' ASC ' + N' UNION' + #query) + ' IS NOT NULL ORDER BY ' + #IdxColumn
SET #SQL = 'SELECT * FROM ('+#SQL+') T ORDER BY ' + #IdxColumn + ' ASC'
EXEC (#SQL)
The following query should work for the sample data:
WITH cte AS (
SELECT *
, DENSE_RANK() OVER (ORDER BY RPMA DESC) AS RPMA_RANK
, DENSE_RANK() OVER (ORDER BY ROP DESC) AS ROP_RANK
, DENSE_RANK() OVER (ORDER BY WOB DESC) AS WOB_RANK
FROM t
)
SELECT Depth
, CASE WHEN RPMA_RANK <= 2 THEN RPMA END
, CASE WHEN ROP_RANK <= 2 THEN ROP END
, CASE WHEN WOB_RANK <= 2 THEN WOB END
FROM cte
WHERE RPMA_RANK <= 2
OR ROP_RANK <= 2
OR WOB_RANK <= 2
Note that it returns three rows for RPMA column (there are two 72 and one 70). For n number of columns you need to use dynamic SQL.
This doesn't answer the question, but does fix the terrifying security flaws in the above.
There are multiple problems with the above, so please note that this is a significant but needed change to the SQL you have. Right now you are injecting unsantised parameters into your code, and also using datatypes that are vastly too large. #Columns is varchar(MAX), meaning that someone has 2GB worth of characters to inject into your system. #IdxColumn is a varchar(250) and references a single column; an object can at most be 128 characters long so there's no need for the other 122 characters. Also #Limit is a varchar, despite being an int and should be a parameter.
Firstly, rather than using a varchar(MAX) for #Columns I suggest a table type object:
CREATE TYPE dbo.ObjectList (ObjectName sysname);
sysname is a synonym of nvarchar(128) NOT NULL; and is the data type used for object names in SQL Server. You'll then need to INSERT the names of the columns into a declared table type parameter; one row for each Column Name
Then we can safely inject and parametrise your query:
--Parameters
DECLARE #Columns dbo.ObjectList,
#IdxColumn sysname, --sysname as well
#Limit int; --not varchar
--Variables needed in the SQL:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT TOP (#Limit)' + #CRLF +
N' ' + QUOTENAME(#IdxColumn) + N',' + #CRLF +
STUFF((SELECT N',' + #CRLF +
N' ' + QUOTENAME(C.ObjectName)
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + #CRLF +
N'FROM dbo.[Table]' + #CRLF + --Should dbo.[Table] also not be safely injected?
N'WHERE ' +
STUFF((SELECT #CRLF +
N' OR ' + QUOTENAME(C.ObjectName) + N' IS NOT NULL'
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,8,N'') + #CRLF +
N'ORDER BY ' + QUOTENAME(#IdxColumn) + N' ASC;'
EXEC sp_executesql #SQL, N'#Limit int', #Limit;

Moving result from 1:n query to repeating columns

I would like to achieve the following from a result of a 1:n join using T-sql
Surname | Givename |..Address | City
Name1....| Givename1.|..Addr11...| City11
Name1....| Givename1.|..Addr12...| City12
Name2....| Givename2.|..Addr21...| City21
Name2....| Givename2.|..Addr22...| City22
Name2....| Givename2.|..Addr23...| City23
TO:
Surname | Givename.. | Address | City... | Address | City... | Address | City
Name1....| Givename1...| Addr11....| City11. | Addr12....| City12. |
Name2....| Givename2...| Addr21....| City21. | Addr22....| City22. | Addr23....| City23
I not care about repeating columnames. Up if there is a soultion with numbers in the repeating columns it would be fine too.
Thanks
In my opinion Pivot is not a solution in this case. Because the column name should repat, and in pivot, cell values are moved to column names, also is unlike pivot no aggregate function involved.
In my thought, the following query will handle your issue. However the SQL Server has column number limit for tables.
Columns per table 1,024 Tables that include sparse column sets
include up to 30,000 columns. See sparse column sets.
You should take this considiration for your data.
DROP TABLE IF EXISTS #Test
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE #Test(Surname VARCHAR(100) , GivenName VARCHAR(200),
Adress VARCHAR(100),City VARCHAR(100))
INSERT INTO #Test
VALUES
('Name1','Givename1','Addr11','City11'),
('Name1','Givename1','Addr12','City12'),
('Name2','Givename2','Addr21','City21'),
('Name2','Givename2','Addr22','City21'),
('Name2','Givename2','Addr23','City23')
,('Name5','Givename5','Addr51','City51'),
('Name5','Givename5','Addr52','City52'),
('Name5','Givename5','Addr53','City53'),
('Name5','Givename5','Addr54','City54'),
('Name3','Givename3','Addr31','City31')
DECLARE #Counter AS INT=1
DECLARE #Max AS INT
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Max= MAX(DetMax) FROM
(
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS DetMax FROM #Test
) AS TMP
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE ##PivotUnlimited (Surname VARCHAR(100),GivenName VARCHAR(100))
WHILE #Counter <=#Max
BEGIN
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD ADDR' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD City' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #Counter=#Counter+1
END
INSERT INTO ##PivotUnlimited (Surname,GivenName)
SELECT DISTINCT Surname , GivenName FROM #Test
DECLARE #Name AS VARCHAR(100)
DECLARE cursorT CURSOR
FOR
SELECT DISTINCT Surname from #test
OPEN cursorT
FETCH NEXT FROM cursorT INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #CounterCursor AS INT=1
DECLARE #MaxCursort AS INT =0
DECLARE #UpdateSQL AS VARCHAR(MAX)
DECLARE #UptAdr AS VARCHAR(100)
DECLARE #UptCity AS VARCHAR(100)
SELECT #MaxCursort=RowNumber FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber FROM #Test
WHERE Surname=#Name
) AS TMP_TBL
WHILE #CounterCursor<= #MaxCursort
BEGIN
SELECT #UptAdr=Adress ,#UptCity=City FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber,* FROM #Test
) AS TMP_TBL WHERE RowNumber=#CounterCursor and Surname=#Name
SET #UpdateSQL= 'UPDATE ##PivotUnlimited SET ADDR' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptAdr +'''' + ' , '
+ ' City' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptCity +'''' + ' WHERE Surname = ' + '''' + #Name + ''''
EXEC (#UpdateSQL)
SET #CounterCursor=#CounterCursor+1
END
FETCH NEXT FROM cursorT INTO #Name
END
CLOSE cursorT
DEALLOCATE cursorT
SELECT * FROM ##PivotUnlimited
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Surname | GivenName | ADDR1 | City1 | ADDR2 | City2 | ADDR3 | City3 | ADDR4 | City4 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Name1 | Givename1 | Addr11 | City11 | Addr12 | City12 | NULL | NULL | NULL | NULL |
| Name2 | Givename2 | Addr21 | City21 | Addr22 | City21 | Addr23 | City23 | NULL | NULL |
| Name3 | Givename3 | Addr31 | City31 | NULL | NULL | NULL | NULL | NULL | NULL |
| Name5 | Givename5 | Addr51 | City51 | Addr52 | City52 | Addr53 | City53 | Addr54 | City54 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+

How to export report and mark a column with data?

This is hard to explain but I'll try. I need to export a report that shows which stores have locations in which states.
Suppose I have the following table:
+----------+-----------+
| STORE_ID | STATE_ABV |
+----------+-----------+
| 1 | AK |
| 1 | AL |
| 1 | AR |
| 2 | MI |
| 2 | OH |
| 2 | IN |
| 3 | CA |
| 3 | NV |
+----------+-----------+
The STORE_ID column is a key to another table where I just need to pull out the STORE_NAME column.
+----------+------------+
| STORE_ID | STORE_NAME |
+----------+------------+
| 1 | Walmart |
| 2 | Target |
| 3 | Kroeger's |
+----------+------------+
What I want is to export a list of each store along with columns for all states. If the store is available in that state, I want to place an "X" for the value.
So the desired output looks like this:
+------------+----+----+----+----+----+----+----+----+
| STORE_NAME | AK | AL | AR | CA | IN | OH | MI | NV |
+------------+----+----+----+----+----+----+----+----+
| Walmart | X | X | X | | | | | |
| Target | | | | | X | X | X | |
| Kroeger's | | | | X | | | | X |
+------------+----+----+----+----+----+----+----+----+
Is this possible in SQL Server? How would I write such a query? There should be a column for every STATE_ABV that exists in the table.
As mentioned, what you are after here is to pivot your data. Personally I dislike the PIVOT functionality of SQL Server, and much more prefer using a Cross-Tab (aka conditional aggregation).
As I suspect that this is going to require a dynamic pivot, I've done that as well:
--Sample tables
CREATE TABLE dbo.StoreLocations (StoreID int,
StateAbv char(2));
CREATE TABLE dbo.Stores (StoreID int IDENTITY,
StoreName varchar(20));
GO
--Sample data
INSERT INTO dbo.Stores (StoreName)
VALUES('Walmart'),('Target'),('Kroeger''s');
INSERT INTO dbo.StoreLocations (StoreID,StateAbv)
VALUES(1,'AK'),
(1,'AL'),
(1,'AR'),
(2,'MI'),
(2,'OH'),
(2,'IN'),
(3,'CA'),
(3,'NV');
GO
--Quick sample to get the format right
SELECT S.StoreName,
IIF(COUNT(CASE WHEN SL.StateAbv = 'AK' THEN 1 END) = 0,NULL, 'X') AS AK
FROM dbo.Stores S
LEFT JOIN dbo.StoreLocations SL ON S.StoreID = SL.StoreID
GROUP BY S.StoreName;
GO
--The real solution
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT S.StoreName,' + NCHAR(13) + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
N' IIF(COUNT(CASE WHEN SL.StateAbv = ' + QUOTENAME(SL.StateAbv,'''') + N' THEN 1 END) = 0, NULL,''X'') AS ' + QUOTENAME(SL.StateAbv)
FROM dbo.StoreLocations SL
GROUP BY SL.StateAbv --Could use DISTINCT too
ORDER BY SL.StateAbv
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) +
N'FROM dbo.Stores S' + NCHAR(13) + NCHAR(10) +
N' LEFT JOIN dbo.StoreLocations SL ON S.StoreID = SL.StoreID' + NCHAR(13) + NCHAR(10) +
N'GROUP BY S.StoreName;';
PRINT #SQL; --Your best friend
EXEC sp_executesql #SQL;
GO
--Clean up
DROP TABLE dbo.Stores;
DROP TABLE dbo.StoreLocations;
db<>fiddle
I foolishly assumed that the state was unique in StoreLocations. Ideally, you should have a States table as well, then you don't need to get the distinct states from the StoreLocations table.
Example with a States table: db<>fiddle
Just in case you want the dynamic pivot. Personally, I don't mind PIVOT. It is just another screwdriver in the toolbox.
The UNION ALL portion can be removed if you don't mind NULL values
Example dbFiddle
Declare #SQL varchar(max) = '
Select *
From (
Select A.Store_ID
,A.State_Abv
,B.Store_Name
,Value = ''X''
From StoreLocations A
Join Stores B on A.Store_ID=B.Store_ID
Union All
Select B.Store_ID
,A.State_Abv
,B.Store_Name
,Value = ''''
From (Select Distinct State_Abv from StoreLocations) A
Cross Join Stores B
) A
Pivot (max(Value) For [State_Abv] in (' + Stuff((Select Distinct ',' + QuoteName(State_Abv) From StoreLocations Order By 1 For XML Path('')),1,1,'') + ') ) p
Order By Store_ID
'
Exec(#SQL)
Returns
Option with NULL Values
Declare #SQL varchar(max) = '
Select *
From (
Select A.Store_ID
,A.State_Abv
,B.Store_Name
,Value = ''X''
From StoreLocations A
Join Stores B on A.Store_ID=B.Store_ID
) A
Pivot (max(Value) For [State_Abv] in (' + Stuff((Select Distinct ',' + QuoteName(State_Abv) From StoreLocations Order By 1 For XML Path('')),1,1,'') + ') ) p
Order By Store_ID
'
Returns

Column name in separate table

I need to analyze application data, and it stores data in separate table from definition (column names). Something like this:
Data table1:
id | 1234 | 1235 | 1236
---|------|------|-----
1 | val1 | val2 | val3
Model:
tableID | colID | Name
--------|-------|-----
table1 | 1234 | Name
table1 | 1235 | DOB
table1 | 1236 | Sex
table2 | 1237 | Manager
etc...
I want to get this data like this:
Data:
id | Name | DOB | Sex
---|------|------|-----
1 | val1 | val2 | val3
Is it possible? Additional problem is that in each data table there might be different number of columns.
UPD.
Problem solved by #Prdp
Though, i've changed query a little bit:
DECLARE #col_list VARCHAR(max)
SET #col_list = Stuff((SELECT ',' + Quotename(af.ColID) + ' as ' + Quotename(af.Name)
FROM [Model].dbo.Fields af
WHERE af.[App.ID] = 123
FOR xml path ('')), 1, 1, '')
EXEC('select '+#col_list+' from [Data].dbo.[123]')
As mentioned in comments better to handle this in application layer. If in case you want a sql solution then you need dynamic query
DECLARE #col_list VARCHAR(8000)
SET #col_list = Stuff((SELECT ',' + Quotename(colID) + ' as ' + Quotename(NAME)
FROM Model
WHERE EXISTS (SELECT 1
FROM information_schema.columns
WHERE table_name = 'Data'
AND Cast(colID AS VARCHAR(50)) = column_name)
FOR xml path ('')), 1, 1, '')
EXEC('select '+#col_list+' from data')

left join of 3 tables and display like a dynamic pivot

i have 3 tables:
payorderType :
---------
typeID | TypeName |
1 | accounting |
2 | budget |
----------
step:
----------
StepID | StepName | typeID
1 | payorder | 1
2 | cheque | 1
3 | cheque | 2
----------
user:
----------
userID | StepName | StepID
7878 | payorder | 1
4547 | cheque | 2
6538 | cheque | 1
----------
I want to make a table which users exists in row and columns includes with concat of step and payorderType. same as below:
users | accounting_payorder | accounting_cheque | budget_cheque |
7878 | 1 | 0 | 0 |
4547 | 0 | 1 | 0 |
6538 | 0 | 1 | 0 |
My quesdtion is : if i don't know number of payorderType rows and number of step rows, how should i write it?
My Script is here :
First I create a table in cursor for concat payorderType and step:
CREATE PROC sp_payOrderType
AS
BEGIN
DECLARE a CURSOR
FOR SELECT DISTINCT p.TypeName,s.StepName
FROM
dbo.PayOrderType p LEFT JOIN
dbo.vStep s ON s.TypeID = p.TypeID
FOR READ ONLY
DECLARE #payOrderType NVARCHAR(50),#stepName NVARCHAR(50)
DECLARE #SQL NVARCHAR(MAX)=''
OPEN a
FETCH NEXT FROM a INTO #payOrderType, #stepName
WHILE ##FETCH_STATUS=0
BEGIN
DECLARE #b VARCHAR(max), #b2 VARCHAR(max)
SELECT #b = ISNULL(#b ,'') +'['+ ISNULL(#payOrderType ,'')+ '____'+ISNULL(#stepName ,'')+ ']'+ ' NVARCHAR(1000) ,'
FETCH NEXT FROM a INTO #payOrderType,#stepName
END
CLOSE a
DEALLOCATE a
SELECT #SQL = 'ALTER table AA(' + SUBSTRING(#b,1, LEN(#b)-1) + ')'
SELECT #SQL
END
but i don't know how i should relate rows(userID) with columns ?
You should have determined output structure. Its little risky to have variable output structure.
But here we go:
Make your structure simple (remove most of variables) - create view (or use derived table) payorderType + step
-- should be inner join probably instead of left join
-- if you use left join you have to isnull s.StepName
SELECT
u.userID,
s.StepID,
p.TypeName,
s.StepName,
p.TypeName + '_' + s.StepName StepType,
-- Your column can be like `coalesce(p.TypeName + '_' + s.StepName, p.TypeName, s.StepName) StepType` for left joins
1 Point
FROM dbo.PayOrderType p
INNER JOIN dbo.vStep s ON s.TypeID = p.TypeID
INNER JOIN dbo.user u ON u.StepID = s.StepID
Make your queries more clear (can all yours fields have null values?. Now you can work with only one column/variable.
Now is time for pivot:
SELECT
userID,
[accounting_payorder],
[accounting_cheque],
[budget_cheque]
FROM newview v
PIVOT(MAX(point) FOR StepType in ([accounting_payorder], [accounting_cheque], [budget_cheque])
If its necessary you can use dynamic query:
declare #header varchar(max),
#columns varchar(max)
select #header = coalesce(#header + ', ', '') + 'isnull(''' + StepType + ''', 0) ' + '[' + StepType + ']',
#columns = coalesce(#columns + ', ', '') + '[' + StepType + ']'
from newview
group by StepType
declare #sqlpvt varchar(4000) -- limited by lenght of exec statement
set #sqlpvt = 'select userID, $HEADER FROM newview v PIVOT(MAX(point) FOR StepType in ($COLUMNS)'
-- replace pseudovariables
set #sqlpvt = replace(#sqlpvt, '$HEADER', #header)
set #sqlpvt = replace(#sqlpvt, '$COLUMNS', #columns)
print #sqlpvt
exec (#sqlpvt)
Sorry if somethink is wrong (writed on blind), but i think for guide it's enough. But you should prefer end on step 2 (non-static code is dangerous).