SQL Server convert NULL to empty string in select * - sql

I need to execute the following:
SELECT * FROM [MY_TVF](9186)
FOR XML AUTO, ELEMENTS
And replace all NULL values with an empty string '' to include them in the XML. I know I can spit out the elements with an xsi:nil="true" attribute by setting ELEMENTS XSINIL, but I don't want that.
I found this question: Convert NULL to Empty String SQL Server, where the answer says I can use ISNULL() around my query. I tried it like so:
ISNULL((SELECT * FROM [MY_TVF](9186)),'')
FOR XML AUTO,ELEMENTS
But I can't get it to work. I get the following error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ','.
What can I do to simply replace all NULL values with an empty string so they are included in the XML?
Edit
There is no way to replace the * by column names. There is a reason we use a TVF, there are about 40 columns and they might change over time. My query is just to select everything and return it as XML.

I have deleted my previous answer and here is the latest one:
Declare #ColName as Varchar(max)
SEt #ColName=''
SELECT #ColName= COALESCE( #ColName + ' ISNULL(' +c.name + ','''') ','', '') +
c.name + ', '
FROM sysobjects o
JOIN syscolumns c ON o.id = c.id
WHERE o.xtype = 'U'
AND (o.name ='tbName')
SEt #ColName= ( 'Select ' + SUBSTRING(#ColName,0,LEN(#ColName)-1) + ' FROM tbName')
print #colname
EXEC(#ColName)

Get out of the habit of SELECT *
See if this works.
SELECT Col1,Col2,Col3,Col4,Col5
FROM
(
SELECT
ISNULL(Col1,'') Col1,
ISNULL(Col2,'') Col2,
ISNULL(Col3,'') Col3,
ISNULL(Col4,'') Col4,
ISNULL(Col5,'') Col5
FROM [MY_TVF](9186)
) T
FOR XML AUTO,ELEMENTS

Related

SQL Search for Data in Multiple Columns

Dears,
I have a table as shown below as a sample, and I want to run one query by which i can find all the yellow highlighted ones by using %AAA%.
Instead of running the Where command on each column one by one, I can do one general find option and it will list all the rows.
Thank you in advance!!
You can include all the conditions in one where clause using or:
where col1 like '%aaa%' or
col2 like '%aaa%' or
. . . -- and so on for all the columns
Unpivot the columns and do a WHERE based on that:
select *
from Table
where exists (select 1
from (values (col1), (col2), (col3) ) AS v (allCols) -- etc
where v.allCols like '%aaa%'
);
If you can't be bothered to type them out, try this little query:
select STRING_AGG('(' + c.name + ')', ', ')
from sys.columns c
where c.object_id = OBJECT_ID('Name_Of_Table_Here');
If you are using sql server then you can write dynamic query to do so. Please try below query:
declare #sql as varchar(max);
select #sql = 'select * from [TableName] where '
+ stuff((
select ' or [' + [column_name] + '] like ''%AAA%'''
from information_schema.columns
where table_name = 'TableName'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
This query will return every row in which at least one column contains AAA.
If you are using PostgreSQL, you can use its JSON functionality:
select t.*
from the_table t
where exists (select *
from jsonb_each(to_jsonb(t)) as x(col,val)
where val like '%AAA%');
If you are using Postgres 12 or later you can use a SQL/JSON path expression:
select t.*
from the_table t
where to_jsonb(t) ## '$.* like_regex "AAA" flag "i"'

Return NON-Matched values from the result of XML path

I have a situation where I need to compare two tables where t1.ColumnA = t2.ColumnA and t1.ColumnB<>t2.ColumnB. The caveat to this problem is that t2.ColumnB is using a "FOR XML PATH" to concatenate like values from another table (linked server using OPENQUERY). This all has to be done within a View.
To concatenate the rows, I am using the following code:
SELECT DISTINCT
CAST(A.CHECK_NUMBER AS nvarchar) [CHECK_NUMBER]
,(
SELECT B.INVOICE_NUMBER + '|'
FROM OPENQUERY([SERVER], 'SELECT * FROM CHECK_LISTING ') B
WHERE B.CHECK_NUMBER = A.CHECK_NUMBER
AND (NULLIF (B.INVOICE_NUMBER, '') IS NOT NULL)
FOR XML PATH('')
) [INVOICE_NUMBER]
, (
SELECT LTRIM(RTRIM(B.PURCHASE_ORDER_ID)) + '|'
FROM OPENQUERY([SERVER], 'SELECT * FROM CHECK_LISTING ') B
WHERE B.CHECK_NUMBER = A.CHECK_NUMBER
AND (NULLIF (B.PURCHASE_ORDER_ID, '') IS NOT NULL)
FOR XML PATH('')
) [PURCHASE_ORDER_ID]
FROM OPENQUERY([SERVER], 'SELECT * FROM CHECK_LISTING ') AS A
This works perfectly and concatenates just like it needs to. My problem is that my next view I created was to run that view against a local table to see the difference in the INVOICE_NUMBER.
SELECT
A.EntryID,
A.Check#,
A.CheckDate,
A.CheckAmount,
A.VendorID,
A.VendorName,
B.INVOICE_NUMBER,
A.Invoice#
FROM dbo.APChecks AS A
LEFT JOIN
dbo.CHECKS_Step2 AS B
ON A.Check#=B.CHECK_NUMBER
WHERE (A.Invoice# != B.INVOICE_NUMBER)
When I try to run this, the query takes at least 25+ minutes. I had to stop the query manually. Some concatenated values are longer than 1000 chars. I was told that it was not possible to INDEX on a dynamic entry like an XML PATH.
Any suggestions? Thank you in advance.

SQL Server: Convert single row to comma delimited (separated) format

As the title states, I need help in converting a single row of data E.g,
col1 col2 col3 <-- This are column names
value1 value2 value3
To something like
dataResult <-- this is the column name from running the procedure or call
value1,value2,value3
The requirements are that this call ( or rather procedure) needs to be able to accept the results of sql queries of any column length and is able to convert that row to a comma delimited string format. Been stuck at this for weeks any help would be greatly appreciated...
EDIT*
Assume the unique key is the first column. Also assume that only 1 row will be returned with each query. Multiple rows will never occur.
The idea is to convert that row to a comma separated string without having to select the column names manually (in a sense automatically convert the query results)
You might try it like this:
A declared table variable to mock-up as test table. Be aware of the NULL value in col2!
DECLARE #tbl TABLE(col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100));
INSERT INTO #tbl VALUES('test1',NULL,'test3');
--This is the query:
SELECT
STUFF(
(
SELECT ',' + elmt.value('.','nvarchar(max)')
FROM
(
SELECT
(
/*YOUR QUERY HERE*/
SELECT TOP 1 *
FROM #tbl
/*--------------------*/
FOR XML AUTO ,ELEMENTS XSINIL,TYPE
)
) AS A(t)
CROSS APPLY t.nodes('/*/*') AS B(elmt)
FOR XML PATH('')
),1,1,'')
FOR XML AUTO will return each row as XML with all the values within attributes. But this would omit NULL values. Your returned string would not inlcude the full count of values in this case. Stating ELEMENT XSINIL forces the engine to include NULL values into the XML. This CROSS APPLY t.nodes('/*/*') will return all the elements as derived table and the rest is re-conactenation.
See the double comma in the middle! This is the NULL value of col2
test1,,test3
ATTENTION: You must be aware, that the whole approach will break, if there is a comma part of a (string) column...
Hint
Better was a solution with XML or JSON. Comma separated values are outdated...
Applay the next Approach:-
Use For Xml to sperate comma,
Get Columns Names Via using INFORMATION_SCHEMA.COLUMNS.
According to your need, select TOP (1) for getting First
Row.
Demo:-
Create database MyTestDB
go
Use MyTestDB
go
Create table Table1 ( col1 varchar(10), col2 varchar(10),col3 varchar(10))
go
insert into Table1 values ('Value1','Value2','Value3')
insert into Table1 values ('Value11','Value12','Value13')
insert into Table1 values ('Value21','Value22','Value23')
go
Declare #Values nVarchar(400),
#TableName nvarchar (100),
#Query nvarchar(max)
Set #TableName = 'Table1'
Select #Values = Stuff(
(
Select '+'','' + ' + C.COLUMN_NAME
From INFORMATION_SCHEMA.COLUMNS As C
Where C.TABLE_SCHEMA = T.TABLE_SCHEMA
And C.TABLE_NAME = T.TABLE_NAME
Order By C.ORDINAL_POSITION
For Xml Path('')
), 1, 2, '')
From INFORMATION_SCHEMA.TABLES As T
where TABLE_NAME = #TableName
select #Values = right(#Values,len(#Values)-4)
select #Query = 'select top(1)' + #Values + ' from ' + #TableName
exec sp_executeSQL #Query
Result:-

SQL for concatenating strings/rows into one string/row? (How to use FOR XML PATH with INSERT?)

I am concatenating several rows/strings in an table (on Microsoft SQL Server 2010) into a string by using a method as suggested here:
SELECT ',' + col FROM t1 FOR XML PATH('')
However, if I try to insert the resulting string as (single) row into another table like so:
INSERT INTO t2
SELECT ', ' + col FROM t1 FOR XML PATH('')
I receive this error message:
The FOR XML clause is not allowed in a INSERT statement.
t2 currently has a single column of type NVARCHAR(80). How can I overcome this problem, i.e. how can I collapse a table t1 with many rows into a table t2 with row that concatenates all the strings from t1 (with commas)?
Rather than xml path why not do it like this?
DECLARE #Cols VARCHAR(8000)
SELECT #Cols = COALESCE(#Cols + ', ', '') +
ISNULL(col, 'N/A')
FROM t1
Insert into t2 values(#Cols);
You need to cast it back to an nvarchar() before inserting. I use this method, deletes the first separator as well and as I'm doing the , type part, it handles entities correctly.
insert into t2
select stuff((
select ', ' + col from t1
for xml path(''), type
).value('.', 'nvarchar(80)'), 1, 2, '')
So you concatenate all col with prepending comma+space as an xml-object. Then you take the .value() of child with xquery-path . which means "take the child we are at, don't traverse anywhere". You cast it as an nvarchar(80) and replace a substring starting at position 1 and length 2 with an empty string ''. So the 2 should be replaced with however long your separator is.

TSQL Reverse FOR XML Encoding

I am using FOR XML in a query to join multiple rows together, but the text contains quotes, "<", ">", etc. I need the actual character instead of the encoded value like """ etc. Any suggestions?
Basically what you're asking for is invalid XML and luckly SQL Server will not produce it. You can take the generated XML and extract the content, and this operation will revert the escaped characters to their text representation. This revert normally occurs in the presnetaitonlayer, but it can occur in SQL Server itslef by instance using XML methods to extract the content of the produced FOR XML output. For example:
declare #text varchar(max) = 'this text has < and >';
declare #xml xml;
set #xml = (select #text as [node] for xml path('nodes'), type);
select #xml;
select x.value(N'.', N'varchar(max)') as [text]
from #xml.nodes('//nodes/node') t(x);
I have a similar requirement to extract column names for use in PIVOT query.
The solution I used was as follows:
SELECT #columns = STUFF((SELECT '],[' + Value
FROM Table
ORDER BY Value
FOR XML PATH('')), 1, 2, '') + ']'
This produces a single string:
[Value 1],[Value 2],[Value 3]
I hope this points you in the right direction.
--something like this?
SELECT * INTO #Names FROM (
SELECT Name='<>&' UNION ALL
SELECT Name='ab<>'
) Names;
-- 1)
SELECT STUFF(
(SELECT ', ' + Name FROM #Names FOR XML PATH(''))
,1,2,'');
-- 2)
SELECT STUFF(
(SELECT ', ' + Name FROM #Names FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)')
,1,2,'');
-- 2) is slower but will not return encoded value.
Hope it help.