Get header as the first row in dynamic SQL - sql

My table:
id name city
--------------------
1 Jim NewYork
2 Rose London
3 Kitty Seattle
......
The output:
id name city
--------------------
id name city
1 Jim NewYork
2 Rose London
3 Kitty Seattle
I can easily do it in static SQL like this, but how to do it in dynamic SQL?
Declare #sql as varchar(max)='select *';
set #sql =#sql+' from mytable';
exec(#sql)

I am not sure why you want to do that as your id looks int to me. If you want one extra string type value to the column to appear on the top, in that case you have to convert all the int to string.
Still if you want to do that you can write your query like following.
SELECT *
FROM (
SELECT cast(id AS VARCHAR(10)) AS id
,Name
,city
FROM mytable
UNION ALL
SELECT 'id'
,'name'
,'city'
) t
ORDER BY CASE
WHEN t.id = 'id'
THEN 0
ELSE 1
END
Same query can be written as dynamic query like following.
DECLARE #sql AS VARCHAR(max) = 'select * from (
select cast(id as varchar(10)) as id,Name,city';
SET #sql = #sql + ' from mytable union select ''id'',''name'', ''city'')t
order by case when t.id=''id'' then 0 else 1 end';
EXEC (#sql)

I don't know if I am overdoing this. But if you want a pure dynamic way, you can try something like
declare #sql varchar(max),
#colnameHeads varchar(max),
#colnames varchar(max);
-- building the row with column headers
set #colnameHeads = STUFF((
SELECT ',''' + COLUMN_NAME + ''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'<your-table>'
FOR XML PATH('')
), 1, 1, '')
-- casting all the values to varchar to match with header row
set #colnames = STUFF((
SELECT ',' + CONCAT('CAST(', COLUMN_NAME, ' AS VARCHAR(100))')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'<your-table>'
FOR XML PATH('')
), 1, 1, '')
-- dynamic script
set #sql = 'select '+ #colnameHeads + ' union all select '+ #colnames + ' from <your-table>';
EXEC (#sql)

Related

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

How do I use loop to generate column names dynamically?

I have table sdata and it has 35 columns (id, name, TRx1, TRx2, TRx3, TRx4,..., TRx30, city, score, total)
I want to fetch data from the TRx1,...TRx30 columns.
Can I use loop here?
I did following code:
DECLARE #flag INT
DECLARE #sel varchar(255)
DECLARE #frm varchar(255)
SET #flag = 1;
SET #sel = 'select TRx';
SET #frm = ' from sdata';
exec(#sel +
(WHILE #flag <=5
#flag
SET #flag = #flag + 1)
+ #frm)
What wrong am I doing? And how can I resolve this?
If your table name is sdata, this code should work for you:
-- Grab the names of all the remaining columns
DECLARE #sql nvarchar(MAX);
DECLARE #columns nvarchar(MAX);
SELECT #columns = STUFF ( ( SELECT N'], [' + name
FROM sys.columns
WHERE object_id = (select top 1 object_id FROM sys.objects where name = 'sdata')
AND name LIKE 'TRx%' -- To limit which columns
ORDER BY column_id
FOR XML PATH('')), 1, 2, '') + ']';
PRINT #columns
SELECT #sql = 'SELECT ' + #columns + ' FROM sdata';
PRINT #sql;
EXEC (#sql);
Note I included PRINT statements so you could see what's going on. You might want to comment out the EXEC while testing.
This would be much easier to do by just copy/pasting the column names and changing them to be the correct one. However if you must do it this way, I do not advise using a loop at all. This method uses a tally table to generate the columns you want to select (in this example, columns 1 through 30, but that can be changed), then generates a dynamic SQL statement to execute against the SData table:
Declare #From Int = 1,
#To Int = 30,
#Sql NVarchar (Max)
Declare #Columns Table (Col Varchar (255))
;With Nums As
(
Select *
From (Values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) As V(N)
), Tally As
(
Select Row_Number() Over (Order By (Select Null)) As N
From Nums A --10
Cross Join Nums B --100
Cross Join Nums C --1000
)
Insert #Columns
Select 'TRx' + Cast(N As Varchar)
From Tally
Where N Between #From And #To
;With Cols As
(
Select (
Select QuoteName(Col) + ',' As [text()]
From #Columns
For Xml Path ('')
) As Cols
)
Select #Sql = 'Select ' + Left(Cols, Len(Cols) - 1) + ' From SData'
From Cols
--Select #Sql
Execute (#Sql)
Note: The --Select #Sql section is there to preview the generated query before executing it.
You can select the column names like this:
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'my name here'

How to convert horizontal row into vertical

I have a Dynamic query which fetch a row with 62 columns in sql
The query is like this
DECLARE #SQLQuery AS nvarchar(max)
DECLARE #columns nvarchar(max)
SELECT
#columns = ISNULL(#columns + ', ', '') + QUOTENAME(Column_name)
FROM (SELECT
Column_name
FROM
(SELECT
Column_name,
ROW_NUMBER() OVER (PARTITION BY Column_name ORDER BY Column_name) AS Rows
FROM information_Schema.Columns
WHERE Table_Name IN ('Parking', 'AIV_Parking')
AND column_name NOT IN ('IssueNo', 'Issuedate', 'Agency', 'OfficerName', 'OfficerId', 'Beat', 'UNITSERIAL', 'VEHLICNO', 'VEHLICSTATE', 'VEHLICEXPDATE', 'VEHLICTYPE', 'VEHMAKE',
'VEHMODEL', 'VEHBODYSTYLE', 'VEHVIN4', 'VEHVIN', 'ISSUENOCHKDGT', 'VEHCOLOR1', 'VEHCOLOR2', 'PERMITNO', 'VEHLABELNO', 'LOCBLOCK', 'LOCSTREET', 'LOCDESCRIPTOR', 'LOCTRAVELDIR', 'LOCSIDEOFSTREET',
'LOCSUBURB', 'LOCLOT', 'LOCCROSSSTREET1', 'LOCCROSSSTREET2', 'LOCSTATE', 'LOCPOSTALCODE', 'METERNO', 'METERBAYNO', 'REMARK1', 'REMARK2')) p
WHERE Rows = 2) RequiredColumns
EXEC ('SELECT ' + #columns + ' FROM AIV_Parking Where Issueno =''100000600''')
Which gives me below records
So result is Something like below where i get only 1 row at a time.
|ACTLINE1 | ACTLINE2 | AUTOPROC_UNIQUEKEY | COURTADDR| IssueNo|.......
|SUBIACO | LOCAL LAWS| NULL | NULL | 123 |
Now i want this to converted like:
|FieldName | FieldValue
|ACTLINE1 | SUBIACO
|ACTLINE2 | LOCAL LAWS
|ISSUENO | 123
While pivoting these column ,I want only those columns which has value i don't want those column whose value is null or ' '
You need to use UNION ALL and filter the result with a WHERE clause:
DECLARE #SQLQuery AS NVARCHAR(MAX) = ''
SELECT #SQLQuery = #SQLQuery +
'SELECT ''' + COLUMN_NAME + ''' AS FieldName, CAST(' + QUOTENAME(COLUMN_NAME) + ' AS NVARCHAR(MAX)) AS FieldValue
FROM AIV_Parking
WHERE Issueno =''100000600''
UNION ALL' + CHAR(10)
--QUERY HERE TO GET COLUMNS FROM INFORMATION_SCHEMA.COLUMNS
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN('Parking','AIV_Parking')
IF #SQLQuery <> '' BEGIN
-- Remove the last occurence of UNION ALL
SELECT #SQLQuery = LEFT(#SQLQuery, LEN(#SQLQuery) - 11)
SELECT #SQLQuery =
'SELECT * FROM (' + #SQLQuery + ') t WHERE FieldValue IS NOT NULL AND FieldValue <> '''''
EXEC(#SQLQuery)
END
Note that you need to CAST your FieldValue to a NVARCHAR(MAX) or whatever datatype that will not produce a conversion error. This is because when using UNION all columns must have the same datatype, if not, they will be converted to the one with the higher data type precedence.

Getting value from different columns and storing in a string with comma separated in Sql server

My requirement is that i have Table with 5 columns. What I want is to fetch the values from the row and want to store all 5 values in variable with comma separated.
table Name: table1
col1 col2 col3 col4 col5
1 2 3 4 5
Result should be
1,2,3,4,5
Note: I dont want to mention column name like if we use Concat function there we need to mention columns, but here my requirement is I'll only have the table name
Use the table : information_schema.columns
DECLARE #col1 varchar(10)
DECLARE #col2 varchar(10)
DECLARE #col3 varchar(10)
DECLARE #col4 varchar(10)
DECLARE #col5 varchar(10)
SET #col1 = (select column_name from information_schema.columns where table_name = 'table1' and ordinal_position = '1')
SET #col2 = (select column_name from information_schema.columns where table_name = 'table1' and ordinal_position = '2')
SET #col3 = (select column_name from information_schema.columns where table_name = 'table1' and ordinal_position = '3')
SET #col4 = (select column_name from information_schema.columns where table_name = 'table1' and ordinal_position = '4')
SET #col5 = (select column_name from information_schema.columns where table_name = 'table1' and ordinal_position = '5')
DECLARE #sqlText nvarchar(1000);
SET #sqlText = N'SELECT ' + #col1 + ',' + #col2 + ',' + #col3 + ','+ #col4 + ','+ #col5 +' FROM dbo.table1'
Exec (#sqlText)
If you always have exactly 5 columns in your table, this would work.
Note this doesn't refer to the column names
;WITH cte(a,b,c,d,e) as
(
-- can test with this:
--SELECT * FROM (values(1,2,3,4,5)) x(col1,col2,col3,col4,col5)
SELECT * FROM table1
)
SELECT
concat(a, ',', b, ',', c, ',', d, ',', e)
FROM
cte
Result:
1,2,3,4,5
I am always hesitant about giving answers that suggest dynamic SQL, often when dynamic SQL is required, the problem is best solved outside of SQL. Erland Sommarskog has written pretty extensively on the subject - The Curse and Blessings of Dynamic SQL
Anyway, your problem can be solved using dynamic SQL. You can build your SQL using the system views, and concatenate the column name using SQL Server's XML extensions:
DECLARE #TableName SYSNAME = 'dbo.table1';
DECLARE #SQL NVARCHAR(MAX) =
'SELECT CONCAT(' + STUFF(( SELECT ','','',' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE c.object_id = OBJECT_ID(#TableName, 'U')
ORDER BY Column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 5, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
So for this table:
CREATE TABLE dbo.Table1
(
Col1 VARCHAR(10),
Col2 VARCHAR(10),
Col3 INT,
Col4 INT,
Col5 VARCHAR(30)
);
The SQL that is generated is:
SELECT CONCAT([Col1],',',[Col2],',',[Col3],',',[Col4],',',[Col5])
FROM dbo.table1;
Just replace the name of your table for variable #tablename and you should be fine.
NULL values will be translated to a 'NULL' string
SET NOCOUNT ON
DECLARE #tablename nvarchar(50) = 'atable'
DECLARE #colList nvarchar(max)
DECLARE #sql nvarchar(max)
--Select list of colum names
select #colList = coalesce('CAST('+#Collist+'+'', '' ' +'+ CAST(','')+name+' as nvarchar)'
from
(
select name
from sys.columns
where [object_id] = OBJECT_ID(#tablename)
) sub
SET #colList = REPLACE(REPLACE(#colList, 'CAST(', 'ISNULL(CAST('), 'nvarchar)', 'nvarchar), ''NULL'')')
SET #sql = 'SELECT '+#colList+' FROM '+#tablename
EXEC(#sql)
Example:
This table (atable)
col1 col2
-------------------------------------------------- -----------
foo 1
bar 2
abc 3
def 4
ghi 5
yep NULL
will be transformed to
--------------------------------------------------------------
foo, 1
bar, 2
abc, 3
def, 4
ghi, 5
yep, NULL
returns 1 result per row of the original table.

Selecting columns from a query

I'm using a request to get a collection of columns name:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE [...]
From this collection, I'd like to count every not null, not empty value from the original table group by column name.
Let's say I have a table containing
COL1 | COL2 | COL3
------------------
VAL1 | VAL2 | NULL
VAL3 | | VAL4
VAL5 | |
I'm looking for a request to get:
COL1 | 3
COL2 | 1
COL2 | 1
It's for analytics purpose.
Thanks for your help!
Here is a simple process. Run the following query:
SELECT 'SELECT ''' + COLUMN_NAME + ''', COUNT(['+COLUMN_NAME']) as NotNull FROM [' +SCHEMA_NAME+ '].['+TABLE_NAME+ '] union all '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE [...]
Copy the results into a query window, remove the final union all, and run the query.
The below code seems to work for your issue
create table sample
(
col1 varchar(10),
col2 varchar(10),
col3 varchar(10)
)
INSERT INTO sample (COL1,COL2,COL3) VALUES ('VAL1 ',' VAL2 ',NULL);
INSERT INTO sample (COL1,COL2,COL3) VALUES ('VAL3 ',' ',' VAL4');
INSERT INTO sample (COL1,COL2,COL3) VALUES ('VAL5 ',' ',' ');
DECLARE #cols1 NVARCHAR(MAX);
DECLARE #sql NVARCHAR(MAX);
SELECT #cols1 = STUFF((
SELECT ', COUNT(CASE WHEN len(['+ t1.NAME + '])!=0 THEN 1 END) AS ' + t1.name
FROM sys.columns AS t1
WHERE t1.object_id = OBJECT_ID('sample')
--ORDER BY ', COUNT([' + t1.name + ']) AS ' + t1.name
FOR XML PATH('')
), 1, 2, '');
SET #sql = '
SELECT ' + #cols1 + '
FROM sample
'
EXEC(#sql)
Hereis my little longer take on this:
declare #cols table (colID integer, colName varchar(50))
declare #results table (colName nvarchar(50), valueCount bigint)
-- table name
declare #tableName nvarchar(50) = 'INSERT TABLE NAME HERE'
-- select column names from table
insert into #cols
select column_id, name from sys.columns where object_id = object_id(#tableName) order by column_id
declare #currentColID int = 0
declare #currentName nvarchar(50) = ''
declare #currentCount bigint = 0
declare #sql nvarchar(max) -- where the dynamic sql will be stored
-- go through all columns
while (1 = 1)
begin
-- step by id
select top 1 #currentColID = c.colID, #currentName = c.colName from #cols c
where c.colid > #currentColID order by c.colID
if ##ROWCOUNT = 0 break;
-- dynamic query to get non-empty, not-null counts
select #sql = 'select #p1=COUNT(' + #currentName + ') from ' + #tableName +
' where ' + #currentName + ' is not null or LEN(' + #currentName + ') > 0'
exec sp_executesql #sql, N'#p1 bigint output', #p1 = #currentCount output
-- insert result to buffer
insert into #results values (#currentName, #currentCount)
end
-- print the buffer
select * from #results
Have fun :)