Return NON-Matched values from the result of XML path - sql

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.

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

Dynamic delete based on update table

I want to write a dynamic script that removes duplicates. I want to try and avoid a CURSOR so I've been looking into writing strings instead that will have table in one column and corresponding table attributes in another. I have also tried dynamic SQL using WITH. But this is what I have so far. This I intend to use as parameters in dynamic SQL later on
STUFF example. However this results in repeating the same column names for every row:
select name as table_name,
stuff(( select ', ' +char(10)+ ac.[name] FROM DW.sys.columns ac
inner join DW.sys.tables t on ac.object_id=t.object_id
where ac.name not in ('ModifiedOn','ValidFrom','ValidTo')
FOR XML PATH('')
), 1, 1, '')
from sys.tables
What I want is this output:
TableName || ColumnName
table1 || aa,ab,ac
table2 || ba,bb,bc
table3 || ca,cb,cc
My idea is to use this to this effect or similair:
'WITH DELETEDUPLICATE AS (
SELECT '+#ColumnName+',
ROW_NUMBER() OVER(PARTITION BY '+#ColumnName+' ORDER BY '+#ColumnName+') AS Duplicate_Row_Count
FROM '+#TableName+'
)
DELETE
FROM DELETEDUPLICATE
WHERE Duplicate_Row_Count > 1
Any ideas appreciated!
UPDATE:
With satishcse's suggestion i get the table I wanted. I had problem with getting multiple rows in the dynamic WITH step so I just removed that part as a varaible (removed away 'SET #WITH =' ). But how to execute every row? what i get now is:
WITH DELETEDUPLICATE AS(....
For every table per row
In OpenQuery you have to run the query using execute() function. The answer can solve your problem, but I do not suggest you use OpenQuery.
declare #query as nvarchar(max)
set
#query =
'WITH DELETEDUPLICATE AS (
SELECT '+#ColumnName+',
ROW_NUMBER() OVER(PARTITION BY '+#ColumnName+' ORDER BY '+#ColumnName+') AS Duplicate_Row_Count
FROM '+#TableName+'
)
DELETE
FROM DELETEDUPLICATE
WHERE Duplicate_Row_Count > 1'
execute(#query)
try the following for the first part:
select name as table_name,
stuff(( select ', ' +char(10)+ ac.[name] FROM DW.sys.columns ac
inner join DW.sys.tables t on ac.object_id=t.object_id
where ac.name not in ('ModifiedOn','ValidFrom','ValidTo')
and st.name = t.name
order by 1
FOR XML PATH('')
), 1, 1, '')
from sys.tables st

sql server column value to be converted in comma seperated

Before this question is marked as duplicate, i know how it can be done but without doing a declare statement i want to do it within a query itself
like i have this query
select distinct costcenterid,costcentername,costcenterdesc,contactid,expirationdate,portal_id,
active,customername,branchid,id from costcenter cc
inner join branchesinportals bp on bp.portalid = cc.portal_id
the branchid and the id fields have different values but all other rows have same values so if i remove those and do a distinct it works good, i get one record
i want that it should always return me one record and combine the columns branchid and id as a comma separated values
i tried looking a this link which seems to be working but how can i integrate that link code with query
http://www.codeproject.com/Tips/635166/SQL-Column-Values-as-Comma-Separated-String
You can use FOR XML to solve this problem. Here is a list of column names (you can run it in any SQL Server Database):
Select Stuff((
Select ', ' + cast(COLUMN_NAME as varchar(max))
From INFORMATION_SCHEMA.COLUMNS
For XML PATH('')
), 1, 2, '');
Here is how to have a one-to-many value set show up:
Select Distinct C1.TABLE_NAME,
Stuff((
Select ', ' + Cast(COLUMN_NAME as VarChar (Max))
From INFORMATION_SCHEMA.COLUMNS C2
Where C1.TABLE_NAME = C2.TABLE_NAME
For Xml Path ('')
), 1, 2, '') Columns
From INFORMATION_SCHEMA.COLUMNS C1
Here is the output from my master database tables and columns:

SQL Server convert NULL to empty string in select *

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

SQL Query to List

I have a table variable in a stored procedure. What I want is to find all of the unique values in one column and join them in a comma-separated list. I am already in a stored procedure, so I can do it some way that way; however, I am curious if I can do this with a query. I am on SQL Server 2008. This query gets me the values I want:
SELECT DISTINCT faultType FROM #simFaults;
Is there a way (using CONCAT or something like that) where I can get the list as a single comma-separated value?
This worked for me on a test dataset.
DECLARE #MyCSV Varchar(200) = ''
SELECT #MyCSV = #MyCSV +
CAST(faulttype AS Varchar) + ','
FROM #Simfaults
GROUP BY faultType
SET #MyCSV = LEFT(#MyCSV, LEN(#MyCSV) - 1)
SELECT #MyCSV
The last part is needed to trim the trailing comma.
+1 to JNK - the other common way you will see, which doesn't require a variable is:
SELECT DISTINCT faulttype + ','
FROM #simfaults
FOR XML PATH ('')
Note that if faulttype contains characters like "<" for example, those will be xml encoded. But for simple values this will be OK.
this is how we do this
create table #test (item int)
insert into #test
values(1),(2),(3)
select STUFF((SELECT ', ' + cast(Item as nvarchar)
FROM #test
FOR XML PATH('')), 1, 2, '')
Without the space after the comma it would be;
select STUFF((SELECT ',' + cast(Item as nvarchar)
FROM #test
FOR XML PATH('')), 1,1, '')