How to convert XML type return text into select columns - sql

I'm trying to get the column names of a table using XML datatype and information_schema columns. When I tried to use the result in another select statement, I have the results with the repeated column name instead of the results set. I have even tried to cast it to varchar but it still failed. what have done wrong ?
DECLARE #TSQL1 varchar(1000);
SELECT #TSQL1 = CAST((SELECT SUBSTRING((SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM [ProdLS].[ information_schema.columns]
WHERE table_name = 'roles'
ORDER BY ORDINAL_POSITION
FOR XML PATH('')), 3, 200000)) AS varchar(max));
SELECT #TSQL1
FROM [aubeakon_scrm4].[acl_roles]
My query to get the results from roles table using the column name retrieved from.

You cannot execute dynamic SQL like that. You need to use sp_executesql. You also need to declare dynamic SQL as nvarchar(max).
You should also use .value to unescape the XML
DECLARE #TSQL1 nvarchar(max) = N'
SELECT
' + STUFF((
SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM [ProdLS].[information_schema].columns
WHERE table_name = 'roles'
ORDER BY ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('text()[1]', 'nvarchar(max)'), 1, LEN(', '), '') + '
FROM [aubeakon_scrm4].[acl_roles];
';
EXEC sp_executesql #TSQL1;

Related

More than VARCHAR(MAX) values in SQL Server

I have some tables with more than 700 columns/1000 columns.
Now I want to display all columns from this table to ISNULL(col1,0) format because when I use PIVOT/UNPIVOT and if there are some NULL values then they wont be converted to NULL and becomes empty strings. So I am trying to replace those NULLs with 0.
In this example I used sysobjects table so that you can try it in your ssms.
The result of this is incomplete as neither VARCHAR(MAX) nor NVARCHAR(MAX) is enough. How do I get all rows rather than few rows here?
DECLARE #colsUnpivot VARCHAR(MAX)
SET #colsUnPivot = STUFF((
SELECT ',' + 'ISNULL(' + QUOTENAME(name) + ', 0) AS '
+ QUOTENAME(name)
FROM sysobjects t
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
PRINT #colsUnPivot
set #query = 'SELECT id,code_name,lkp_value
FROM
(
SELECT unitid,institution,city,state,zip, '+ #colsUnpivot+'
FROM sysobjects) AS cp
UNPIVOT (lkp_value for code_name IN ('+#colsUnPivot+')
) AS up'
--PRINT #Query
--PRINT #Query1
exec(#query)
I mean the code above does not make sense but I can not produce same thing that I have as i have to use sysobjects here.
But above code is throwing an error:
Msg 102, Level 15, State 1, Line 6
Incorrect syntax near '('.
And that's because there is so much data and it's being truncated.
Here is what my PRINT says,
,ISNULL([opd_
So I still think its truncating.
Your problem is that the PRINT command will truncate the data for display in SSMS.
I would suggest leaving it as XML and doing a SELECT, if you just need to see the data in SSMS without truncating:
DECLARE #colsUnpivot xml
SET #colsUnPivot = (SELECT ',' + 'ISNULL(' + QUOTENAME(name) + ', 0) AS '
+ QUOTENAME(name)
FROM sysobjects t
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)')
SELECT #colsUnPivot
SSMS treats XML output differently and has a higher threshold for truncating the data.
Use SELECT instead of print in your SQL.
SELECT #colsUnPivot
Also, make sure that these values are maxed out in Results to Grid:

Query Result(NULL value/Datetime format)

I have this query that I use for put all the rows in one column, is dynamic because I need to do this with at least 8 tables:
DECLARE #tblName VARCHAR(20) = 'Location'
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX)
SELECT #columns = COALESCE(#columns, '') + '+[' + COLUMN_NAME + '],+'''''','''''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tblName and TABLE_SCHEMA='LES'
select #columns
SET #sql = 'SELECT CONCAT(''''''''' + STUFF(#columns, 103,9, '') + '+'''''') FROM ' + #tblName
select #sql
--------------------------------------------------------------------------
R1: SELECT CONCAT(''''+[Location],+''','''+[Location Type],+''','''+[Region],+''','''+[World Region],+''','''+[Refresh Date]+''') FROM Location
If I execute the query (without the datetime column (Refresh Date) that contains NULL values) the result will be
'0020319389','CMF','AJ','AJ'
'0031209263','CMF','AJ','AJ'
'01BM','DCL','EU','EU'
'01CR','DCL','EU','EU'
My problem here is that when I execute the query with the Refresh_date column, I get this error:
Conversion failed when converting date and/or time from character string.
Can anybody help me please?
Thanks
Your problem is that you're mixing old school concatenation, + with the 2012/2014 CONCAT function and data precedence rules are in effect.
This bit of code is using old school syntax
SELECT #columns = COALESCE(#columns, '') + '+[' + COLUMN_NAME + '],+'''''','''''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tblName and TABLE_SCHEMA='LES'
Instead, make it use a CONCAT, then your data type precedence will convert your date times to a string as well as handle NULLs.

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'.

Error with Dynamic SQL statement cutting off with exec command

I am attempting to pivot a column with 400 or so unique values with the following code:
Declare #t VARCHAR(10)
Declare #A VARCHAR(1000)
Declare #B VARCHAR(1000)
set #A='SELECT Name, IRIS_ID__c'
SET #B='('
SELECT #A=#A+',['+Question_Concept_With_ImpactArea__c+'] as ['+Question_Concept_With_ImpactArea__c+']',#B=#B+'['+Question_Concept_With_ImpactArea__c+']'
FROM (SELECT DISTINCT Question_Concept_With_ImpactArea__c
FROM Company_Number_Response) cur
-- removing last ',' from both variables
SET #B=SUBSTRING(#B,1,LEN(#B)-1)
SET #A=#A+ + '
FROM
(SELECT NAME, IRIS_ID__c, Selected, Question_Concept_With_ImpactArea__c
FROM Company_Number_Response) s PIVOT (max(Unified_Response__c)
FOR Question_Concept_With_ImpactArea__c IN ' +#B+')) p ORDER BY [IRIS_ID__c];'
exec(#A);
Running this code throws an error that "Unclosed quotation mark after the character string 'CM_PcCOGSSup'
Incorrect Syntax near 'CM_PcCOGSSup'
When I change the last command from exec to print I can see that many of the values are being read, but the statement simply cuts off at the value in the above error. I checked the data and do not see any characters that would be causing this. Additionally, I thought the result might be too long and changed the max number for results to text to 8192.
Any help would be greatly appreciated.
Assuming you're using SQL Server 2005 or up, change your variable declarations to varchar(max). It's cutting off because you've only supplied varchar(1000), and string concatenation does not automatically lengthen char data types--they get truncated.
In SQL 2000 you can only use varchar(8000). If you need longer than that you will have to get creative--a pretty difficult task.
You also have SET #A=#A+ + '. If that's working, it's a surprise to me, but in any case there should only be one + there.
And, you should not just put square brackets around columns to make them a valid sysname data type. You should use the QuoteName function:
SELECT #A = #A + ',' + QuoteName(Question_Concept_With_ImpactArea__c) ...
Try this one -
DECLARE #Columns VARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
SELECT #Columns = STUFF((
SELECT DISTINCT ', ['+ Question_Concept_With_ImpactArea__c + ']'
FROM dbo.Company_Number_Response
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
SELECT #SQL = 'SELECT Name, IRIS_ID__c, ' + #Columns + '
FROM
(
SELECT NAME, IRIS_ID__c, Selected, Question_Concept_With_ImpactArea__c
FROM Company_Number_Response
) s
PIVOT (
max(Unified_Response__c)
FOR Question_Concept_With_ImpactArea__c IN (' + #Columns + ')
) p
ORDER BY [IRIS_ID__c];'
EXEC sys.sp_executesql #SQL

How to select output of XML Path query in 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.