How to select output of XML Path query in SQL? - sql

Doc table contains a lot of columns (even not used ones):
Doc_DdfID Doc_RentDate Doc_ReturnDate etc.
--------- ------------ -------------- ----
1 2012-07-28 2012-07-28
But I want to query just the used ones within Doc's table.
DocDefinitionFields list columsn that are in use by document:
SELECT Dfl_ColName
FROM DocDefinitionFields
WHERE Dfl_DdfID = 1
DocDefinitionFields:
Dfl_ColName
-----------
Doc_RentDate
Doc_ReturnDate
...........
So I want to select all columns (listed by second query) from Doc table.
Example (if 2 columns are added to document definition form I want to select just them):
Doc:
Doc_RentDate Doc_ReturnDate
------------ --------------
2012-07-28 2012-07-28
Tried to do that by subquerying select with concatenation of fields using XML PATH:
SELECT
(SELECT
Dfl_ColName + ', '
FROM DocDefinitionFields
FOR XML PATH('')
)
FROM Doc
It's not that simple tho. What do you suggest?

What you need here is dynamic SQL, something like this:
DECLARE #sql VARCHAR(MAX)
SET #sql = 'SELECT ' + STUFF((SELECT ', ' + Dfl_ColName FROM DocDefinitionFields FOR XML PATH('') ),1,1,'') + ' FROM Doc'
EXEC (#sql)
Also, in order to eliminate additional comma(,) at the end of columns I have added STUFF function along with FOR XML PATH.

To get the column names for a query dynamically and use these in a query you will need to use Dynamic SQL. Below is an example of how to create the string of available columns
DECLARE #Columns VARCHAR(MAX);
SELECT #Columns =
COALESCE(#Columns + ',
[' + CAST(COLUMN_NAME AS VARCHAR) + ']',
'[' + CAST(COLUMN_NAME AS VARCHAR) + ']')
FROM (SELECT DISTINCT COLUMN_NAME
FROM [SomeDatabase].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'SomeTableName') AS H
ORDER BY COLUMN_NAME;
GO
You can now use the string of available columns in a Dynamic SQL query. Below we have adopted the above in an INSERT query that build the required fields dynamically. The reason why we need to do it in the below is the inclusion of the set field SomeFieldA along with the others.
DECLARE #SQL NVARCHAR(MAX);
SET #SQL =
N'INSERT INTO [' + #DbName + N']..[SomeOtherTable] ([SomeFieldA], ' + #Columns + N')
SELECT SomeFieldA, ' + #Columns + N'
FROM [SomeTableName];';
EXEC sp_executesql #SQL;
GO
You should be able to amend the above to provide what you need.
I hope this helps.

Related

SELECT only values defined in INFORMATION SCHEMA

Could you please help me with following issue?
Source table:
Columns defined from INFORMATION_SCHEMA.COLUMNS:
In output I'd like to take my source table, but show only values which column name is the same as column name defined in information schema. Meaning:
Is it possible? Many thanks in advance
You need to use dynamic SQL to do that:
declare #sql varchar(1000) 'select ';
select #sql = #sql + '[' + column_name + '] ,' from INFORMATION_SCHEMA.COLUMNS;
-- remove last character in a string which is comma
select #sql = left(#sql, len(#sql) - 1);
-- you need to change talbe name here
select #sql = #sql + ' from MyTable';
-- execute statement
exec(#sql)

how do I select records that are like some string for any column in a table?

I know that I can search for a term in one column in a table in t-sql by using like %termToFind%. And I know I can get all columns in a table with this:
SELECT *
FROM MyDataBaseName.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'MyTableName`
How can I perform a like comprparison on each of the columns of a table? I have a very large table so I can't just spell out LIKE for each column.
As always, I'll suggest xml for this (I'd suggest JSON if SQL Server had native support for it :) ). You can try to use this query, though it could perform not so well on large number of rows:
;with cte as (
select
*,
(select t.* for xml raw('data'), type) as data
from test as t
)
select *
from cte
where data.exist('data/#*[local-name() != "id" and contains(., sql:variable("#search"))]') = 1
see sql fiddle demo for more detailed example.
Important note by Alexander Fedorenko in comments: it should be understood that contains function is case-sensitive and uses xQuery default Unicode code point collation for the string comparison.
More general way would be to use dynamic SQL solution:
declare #search nvarchar(max)
declare #stmt nvarchar(max)
select #stmt = isnull(#stmt + ' or ', '') + quotename(name) + ' like #search'
from sys.columns as c
where c.[object_id] = object_id('dbo.test')
--
-- also possible
--
-- select #stmt = isnull(#stmt + ' or ', '') + quotename(column_name) + ' like #search'
-- from INFORMATION_SCHEMA.COLUMNS
-- where TABLE_NAME = 'test'
select #stmt = 'select * from test where ' + #stmt
exec sp_executesql
#stmt = #stmt,
#params = N'#search nvarchar(max)',
#search = #search
sql fiddle demo
I'd use dynamic SQL here.
Full credit - this answer was initially posted by another user, and deleted. I think it's a good answer so I'm re-adding it.
DECLARE #sql NVARCHAR(MAX);
DECLARE #table NVARCHAR(50);
DECLARE #term NVARCHAR(50);
SET #term = '%term to find%';
SET #table = 'TableName';
SET #sql = 'SELECT * FROM ' + #table + ' WHERE '
SELECT #sql = #sql + COALESCE('CAST('+ column_name
+ ' as NVARCHAR(MAX)) like N''' + #term + ''' OR ', '')
FROM INFORMATION_SCHEMA.COLUMNS WHERE [TABLE_NAME] = #table
SET #sql = #sql + ' 1 = 0'
SELECT #sql
EXEC sp_executesql #sql
The XML answer is cleaner (I prefer dynamic SQL only when necessary) but the benefit of this is that it will utilize any index you have on your table, and there is no overhead in constructing the XML CTE for querying.
In case someone is looking for PostgreSQL solution:
SELECT * FROM table_name WHERE position('your_value' IN (table_name.*)::text)>0
will select all records that have 'your_value' in any column. Didn't try this with any other database.
Unfortunately this works as combining all columns to a text string and then searches for a value in that string, so I don't know a way to make it match "whole cell" only. It will always match if any part of any cell matches 'your_value'.

SQL results to string with wildcard

Suppose you have a table like this:
ID FNAME LNAME
1 Bob Smith
2 Sally Jones
A simple SELECT * FROM [Table] will return all rows. But what if you wanted to build a single string out of the results, and the column names are unknown? In other words, this will not work:
SELECT ID + ',' + FNAME + ',' + LNAME FROM [Table]
because you don't know the column names. Additionally, COALESCE won't work because it doesn't accept wildcards. Ideally you want to execute something like this:
SELECT dbo.FunctionThatSplitsResultsToString(*) FROM [Table]
and have it return
1,Bob,Smith
2,Sally,Jones
Is this possible?
This is a corrected version of the answer #Igor gave. In addition to concatenating comma characters between the values, it converts NULL values to an empty string (because concatenating a string to NULL results in a NULL value).
DECLARE #sql NVARCHAR(max)='SELECT '
DECLARE #TableName NVARCHAR(max) = 'Table_Name' -- <-- Set the target table name here
SELECT #sql=#sql+N'ISNULL(CAST(' + name +' as NVARCHAR(max)), '''')+'',''+'
FROM sys.columns
WHERE object_id=OBJECT_ID(#TableName)
SELECT #sql=SUBSTRING(#sql,1,LEN(#sql)-5)+N' FROM ' + #TableName
--SELECT #sql -- uncomment to see the query string
EXEC sp_executesql #sql
As the first Igor noted, the solution is dynamic SQL. You need to construct the underlying SQL statement correctly.
The following code casts all columns to varchar() and then concatenates them together. The final form of the SQL removes the last "+" sign and adds the from statement:
declare #sql varchar(max);
select #sql = (select 'cast('+coalesce(column_name, '') + ' as varchar(255)) +'
from information_schema.columns
where table_name = <whatever>
for xml path ('')
);
select #sql = left(#sql, len(#sql - 2)) + ' from t';
exec(#sql);
I admit to being US-centric and rarely using internationalization. The whole thing also works with nvarchars().
Try the below one
GO
DECLARE #ColumnsList VARCHAR(MAX), #SelectStatement VARCHAR(MAX),#TargetTable VARCHAR(250) ,#FINALSQL NVARCHAR(MAX)
SET #TARGETTABLE ='TempData'
SELECT #ColumnsList = COALESCE( #ColumnsList+' + '','' +' ,'') + 'Cast('+ A.COLUMN_NAME + ' AS Varchar(250))'
FROM (select Column_Name from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME= #TARGETTABLE) A
SELECT #FinalSql = 'Select ' +#ColumnsList + ' FROM ' + #TARGETTABLE
EXEC SP_EXECUTESQL #FINALSQL
GO

Include sys.databases name as column in dynamic SQL UNION

I have successfully written code to dynamically union the results of a simple select statement across multiple databases. I would like to include the database name itself as a field so that I can identify each record. What is the best way to modify the code below to accomplish that?
My current results look like
field2, field3, field4
b,c,d
2,3,4
I can't tell what database the row containing (b,c,d) comes from.
And I would like to make sure I see
field1, field2, field3, field4
First_DatabaseName, b,c,d
Second_DatabaseName,2,3,4
And above, I could then see that the row containing (b,c,d) comes from First_Database
DECLARE #sql varchar(max)
SELECT #sql = ISNULL(#sql + 'union all ',' ') + ' SELECT * FROM ' + name + '.dbo.CombinedProvider '
FROM sys.databases
WHERE name in ('First_DatabaseName', 'Second_DatabaseName')
EXEC (#sql)
Just add the name into the subquery that you are using:
SELECT #sql = COALESCE(#sql + 'union all ',' ') + ' SELECT '''+name+''' as dbname, c.* FROM ' + name + '.dbo.CombinedProvider c'
FROM sys.databases
WHERE name in ('First_DatabaseName', 'Second_DatabaseName')
By the way, you are using an unsupported technique to do aggregate string concatenation. You might want to learn about "for xml path('')" as an alternative technique. Check out http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ for more information than you want to know on this subject.
Use 'AS' after each column and include the source db name in the column.
DECLARE #sql varchar(max)
SELECT #sql = ISNULL(#sql + 'union all ',' ') + ' SELECT ColName as [First_DatabaseName_ColName], ColName2 as [Second_DatabaseName_ColName2], FROM ' + name + '.dbo.CombinedProvider '
FROM sys.databases
WHERE name in ('First_DatabaseName', 'Second_DatabaseName')
EXEC (#sql)

Aliasing columns based on cross-reference table

I have a table with over 400 columns, and these are named with our vendor's archaic naming system. I need to move this data into a new table which uses our company's naming conventions, so I have to change the names of these 400 columns.
Fortunately, I also have a table that cross-references the current column names with what they should become, like so:
Acronym | Name
----------------
A | ColumnNameA
B | ColumnNameB
C | ColumnNameC
etc...
So my question is this:
If it were only a few rows, I could easily do
SELECT
A AS ColumnNameA,
B AS ColumnNameB
FROM
Table
But there are just too many columns to do this by hand. What's the best way to dynamically change column names in a SELECT statement based off of a cross-ref table?
My effort so far:
I was thinking something along the lines of
SET #sqlCommand = 'SELECT ' + #columns + ' FROM Table'
EXEC (#sqlCommand)
but I have no idea how to set #columns to be a dynamically generated list of all the acronyms as the final column names. Is this even a viable approach?
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ',' + QUOTENAME(Acronym) + ' AS ' + QUOTENAME(Name)
FROM dbo.AcronymTable;
SET #sql = 'SELECT ' + STUFF(#sql, 1, 1, N'') + ' FROM dbo.Table;';
PRINT #sql;
--EXEC sp_executesql #sql;
The easiest way is to add an int field that determines the order of columns so it matches from source to target. Then you can:
DECLARE #sql varchar(max)
SET #SQL = 'INSERT INTO dbo.Target SELECT '
SELECT #SQL = #SQL + Acronym + ','
FROM ConversionTable
ORDER BY OrderColumn
SET #SQL = LEFT(#SQL, (LEN(#SQL) - 1))
SET #SQL = #SQL + ' FROM SourceTable'