TSQL Compare the result of 2 queries - sql-server

does anyone have ideas how to compare the result of 2 queries, that have the same columns names, but in different order?
I know that if I had both queries returning the same columns in the same order, I could use except, this isn't the case.
[EDIT]
To be more specific, I need to compare the value of each row, and each column (with the same name) from 2 different queries.
Example:
result query 1:
A|B|C|D
1|4|7|11
2|5|8|21
3|**6**|9|31
result query 2:
A|B |D
1|4 |11
2|5 |21
3|**99**|31
In this case, I would like to detect that Query2 on 3ยบ row in column B, have a different value.
I don't care that Query2 don't have the column C, I just want that all common columns, between both queries, have the same values.
Thanks

Given these tables and data:
USE tempdb;
GO
CREATE TABLE dbo.TableA
(
A INT,
B INT,
C INT,
D INT
);
CREATE TABLE dbo.TableB
(
A INT,
D INT,
B INT
);
INSERT dbo.TableA SELECT 1,4,7,11
UNION ALL SELECT 2,5,8,21
UNION ALL SELECT 3,6,9,31;
INSERT dbo.TableB SELECT 1,11,4
UNION ALL SELECT 2,21,5
UNION ALL SELECT 3,31,99;
What you seem to be looking for is one of the following:
-- those where at least one column doesn't match:
SELECT A,B,D FROM dbo.TableA
EXCEPT
SELECT A,B,D FROM dbo.TableB;
Results (from the A side):
A B D
---- ---- ----
3 6 31
OR
-- those where all columns DO match:
SELECT A,B,D FROM dbo.TableA
INTERSECT
SELECT A,B,D FROM dbo.TableB;
Results:
A B D
---- ---- ----
1 4 11
2 5 21
If you don't know the columns or don't want to write them out manually, you can do this with dynamic SQL by just passing the two table names (with schema) into variables. Note that this doesn't trap for the errors that will occur if no columns are shared by the two tables, or if the same column names exist but are of incompatible data types. That error handling is easy to add if you want to make the solution more robust.
DECLARE
#sql NVARCHAR(MAX),
#cols NVARCHAR(MAX),
#t1 NVARCHAR(511),
#t2 NVARCHAR(511);
SELECT
#sql = N'',
#cols = N'',
#t1 = N'dbo.TableA',
#t2 = N'dbo.TableB';
SELECT #cols = #cols + ',' + a.name
FROM sys.columns AS a
INNER JOIN sys.columns AS b
ON a.name = b.name
WHERE a.[object_id] = OBJECT_ID(#t1)
AND b.[object_id] = OBJECT_ID(#t2);
SET #cols = STUFF(#cols, 1, 1, N'');
-- those where at least one column doesn't match:
SELECT #sql = N'SELECT ' + #cols + '
FROM ' + #t1 + ' EXCEPT
SELECT ' + #cols + ' FROM ' + #t2 + ';';
EXEC sp_executesql #sql;
-- those where all columns DO match:
SELECT #sql = N'SELECT ' + #cols + '
FROM ' + #t1 + ' INTERSECT
SELECT ' + #cols + ' FROM ' + #t2 + ';';
EXEC sp_executesql #sql;
Don't forget to clean up:
DROP TABLE dbo.TableA, dbo.TableB;

You can wrap your queries as subqueries and then reselect the columns in any order you want.

You can do it in only one step:
SELECT *
FROM (
--compare query a vs query b
SELECT ad.id_addetto,'not in b'y
FROM addetti ad
WHERE ad.id_addetto < 125 -- query a
EXCEPT
SELECT ad.id_addetto,'not in b'y
FROM addetti ad
WHERE ad.id_addetto < 166 -- query b
UNION
--compare query b vs query a
SELECT ad.id_addetto, 'not in a'y
FROM addetti ad
WHERE ad.id_addetto < 166 -- query b
EXCEPT
SELECT ad.id_addetto ,'not in a'y
FROM addetti ad
WHERE ad.id_addetto < 125 -- query a
) xx

Related

How to merge two rows by updating only NULL values in TSQL?

I would like to merge two rows of data, by keeping a row based on its ID and only updating data if it is a NULL value.
As an example I want to "merge" row 1 and 2 and delete row 2:
From :
ID date col1 col2 col3
---------------------------------------------------------------
1 31/12/2017 1 NULL 1
2 31/12/2015 3 2 NULL
3 31/12/2014 4 5 NULL
To:
ID date col1 col2 col3
---------------------------------------------------------------
1 31/12/2017 1 2 1
3 31/12/2014 4 5 NULL
In the example I want to keep row 1, and fill NULL values in row 1 by values that are in row 2. Then I will delete row 2. See below the code I have made for the date column.
UPDATE MyTable
SET
date = newdata.date
FROM
(
SELECT
date
FROM MyTable
WHERE
ID = 2
)
newdata
WHERE
ID = 1 AND MyTable.date IS NULL ;
I would like to perform the same operation on very large tables so I'm looking for a way to apply the above operation automatically (or a better workaround?) to every column of a table for two specific rows.
To be clear, the column name (date) shouldn't be hardcoded as in the above example as I have plenty of different tables.
The table has many rows but I only want to merge two rows (this will always be two rows)
Could you help me with this ?
I'm posting this an an answer now, as the comments from the OP do seem to infer this really is as simple as I thought it wasn't. Although their table has a lot of rows, they are only interested in correcting/merging the values of row 1 and 2. As these rows are simplistic then you can simply UPDATE the value of ID 1, and then DELETE row 2.
As there's only a few columns, then you could simply use literal values, as we can visually see that only Col2 on ID 1 needs to be updated:
UPDATE YourTable
SET col2 = 2
WHERE ID = 1;
Now ID 1 has the correct value, you can DELETE ID 2:
DELETE
FROM YourTable
WHERE ID = 2;
You could, however, do the following, if you're data is (a little) over simplified.
UPDATE YT1
SET Col1 = ISNULL(YT1.Col1,YT2.Col1),
Col2 = ISNULL(YT1.Col2,YT2.Col2),
Col3 = ISNULL(YT1.Col3,YT2.Col3),
...
FROM YourTable YT1
JOIN YourTable YT2 ON YT2.ID = 2
WHERE YT1.ID = 1;
DELETE
FROM YourTable
WHERE ID = 2;
This is based on all the comments under the OP's question, that give some more (but not enough) detail. This is a dynamic SQL solution that is scalable, as it writes out the ISNULL expressions for the OP. Of course, if this doesn't help then once again I have the suggest they update their post to actually help us help them. Anyway, this should be self explanatory:
CREATE TABLE YourTable (ID int,
[date] date,
col1 int,
col2 int,
col3 int,
col4 int,
col5 int);
GO
INSERT INTO YourTable
VALUES (1,'20171231',1,NULL,1 ,2 ,NULL),
(2,'20151231',3,2 ,NULL,NULL,4),
(3,'20141231',4,5 ,NULL,2 ,7);
SELECT *
FROM YourTable;
GO
DECLARE #SQL nvarchar(MAX);
DECLARE #TableName sysname = N'YourTable'
DECLARE #CopyToId int = 1;
DECLARE #DeleteID int = 2;
SET #SQL = N'UPDATE YT1' + NCHAR(10) +
N'SET ' + STUFF((SELECT N',' + NCHAR(10) +
N' ' + QUOTENAME(c.[name]) + N' = ISNULL(YT1.' + QUOTENAME(c.[name]) + N',YT2.' + QUOTENAME(c.[name]) + N')'
FROM sys.tables t
JOIN sys.columns c ON t.[object_id] = c.[object_id]
WHERE t.[name] = #TableName
AND c.name NOT IN (N'ID',N'date')
FOR XML PATH(N'')),1,6,N'') + NCHAR(10) +
N'FROM ' + QUOTENAME(#TableName) + N' YT1' + NCHAR(10) +
N' JOIN ' + QUOTENAME(#TableName) + N' YT2 ON YT2.ID = #dDeleteID' + NCHAR(10) +
N'WHERE YT1.ID = #dCopyToId;' + NCHAR(10) + NCHAR(10) +
N'DELETE' + NCHAR(10) +
N'FROM ' + QUOTENAME(#TableName) + NCHAR(10) +
N'WHERE ID = #dDeleteID;';
PRINT #SQL; --Your Best friend
EXEC sp_executesql #SQL, N'#dCopyToID int, #dDeleteID int', #dCopyToId = #CopyToId, #dDeleteID = #DeleteID;
GO
SELECT *
FROM YourTable;
GO
DROP TABLE YourTable;

Get data from every column in every table in a database | SQL Server

I have seen multiple questions on how to retrieve every column from every table along with its data type, among many other pieces of information which can be summarised in the shortest way with this query:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
However, is it possible to get all the data from the columns and the rows they belong to get the first row in the table alongside this? I have not found a way to do so thus far. Is it possible to do such, maybe also having a WHERE condition such as checking if the table contains a list of specific columns before returning it e.g.:
SELECT <AllTablesAndColumns+FirstRow>
FROM <WhereTheyCanBeSelectedFrom>
WHERE <TheTableHasTheseSpecificColumns>
Which would return the table name, column name and the data contained within those columns for each row.
If you are looking for more of an EAV structure
Let's say that we're looking for all tables with a column name of ZIPCODE
Example
Declare #S varchar(max) = ''
SELECT #S = #S +'+(Select top 1 SourceTable='''+A.Table_Name+''',* from '+quotename(A.Table_Name)+' for XML RAW)'
FROM INFORMATION_SCHEMA.COLUMNS A
Where COLUMN_NAME in ('ZipCode')
Declare #SQL varchar(max) = '
Declare #XML xml = '+stuff(#S,1,1,'')+'
Select SourceTable = r.value(''#SourceTable'',''varchar(100)'')
,Item = attr.value(''local-name(.)'',''varchar(100)'')
,Value = attr.value(''.'',''varchar(max)'')
From #XML.nodes(''/row'') as A(r)
Cross Apply A.r.nodes(''./#*'') AS B(attr)
Where attr.value(''local-name(.)'',''varchar(100)'') not in (''SourceTable'')
'
Exec(#SQL)
Returns
You could build dynamic query:
DECLARE #sql NVARCHAR(MAX) =
N'SELECT *
FROM (VALUES (1)) AS s(n)
<joins>';
DECLARE #joins NVARCHAR(MAX)= '';
SELECT #joins += FORMATMESSAGE('LEFT JOIN (SELECT TOP 1 * FROM %s ) AS sub%s
ON 1=1' + CHAR(10), table_schema + '.' + table_name,
CAST(ROW_NUMBER() OVER(ORDER BY 1/0) AS VARCHAR(10)))
FROM (SELECT DISTINCT table_schema, table_name
FROM INFORMATION_SCHEMA.COLUMNS
-- WHERE ... -- custom logic based on column type/name/...
) s;
SET #sql = REPLACE(#sql, '<joins>', #joins);
PRINT #sql;
EXEC(#sql);
DBFiddle Demo
The dynamic query has structure:
SELECT *
FROM (VALUES (1)) AS s(n) -- always 1 row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab1 ) AS sub1 ON 1=1 -- get single row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab2 ) AS sub2 ON 1=1
LEFT JOIN (SELECT TOP 1 * FROM dbo.tabC ) AS sub3 ON 1=1
Please treat it as starting point. You could easily extend it with WHERE condition for each subquery and return specific columns instead of *.
EDIT:
Version with UNION ALL:
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = COALESCE(#sql + ' UNION ALL', '') +
FORMATMESSAGE(' SELECT TOP 1 tab_name=''%s'',col_name=''%s'',col_val=%s FROM %s'+CHAR(10)
,table_name, column_name, column_name, table_schema + '.' + table_name)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'colV%';
PRINT #sql;
EXEC(#sql);
DBFiddle Demo2

t-sql Pivot on all values

I have a table with around 10 rows. I want to pivot on all values in one column to a one-row multi column result. It looks as though there is no way to get around the "For ContactTypeID in ([1],[2])" syntax.
ContactTypeID int
ContactType varchar(20)
Sample data:
1 Customer
2 Vendor
...
5 BillTo
I want to return a single row with
Customer Vendor BillTo, etc
1 2 5
But like I said, I don't want to have to specify each ContactTypeID by number. Is there way to specify "for all"?
Thank you.
You need a dynamic pivot.
Here's the code, for your reference. Hope it helps.
CREATE TABLE tablename (ContactTypeID int, ContactType varchar(20));
INSERT INTO tablename VALUES (1, 'Customer'), (2, 'Vendor'), (5, 'BillTo');
DECLARE #cols NVARCHAR (MAX);
SELECT #cols = COALESCE (#cols + ',[' + ContactType + ']',
'[' + ContactType + ']')
FROM (SELECT DISTINCT [ContactType] FROM tablename) PV
ORDER BY [ContactType]
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT * FROM tablename
) x
PIVOT
(
MIN(ContactTypeID)
FOR [ContactType] IN (' + #cols + ')
) p
'
EXEC SP_EXECUTESQL #query;

sql server 2008 pivot more than one column and dynamic column

I have two tables. I write a query. Date is dynamic. I can select any date. `
select a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) as
EntryDate,b.actualtime from mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and
CONVERT(varchar(12),b.EntryDate,101) between
CONVERT(varchar(12),GETDATE()-5,101) and
CONVERT(varchar(12),GETDATE()+25,101)`
Output
But i want output like
Assuming that i am storing your result in one temp table and imaging data i created one data for your requirement
try this one whether it is useful or not
create table #piv
(
mp varchar(10),
cp varchar(10),
freq varchar(10),
time int,
entryd date,
acuralize int
)
insert into #piv values
('don','asper','da',30,getdate(),0),
('dwm','donl','da',10,getdate(),3),
('qar','qpr','da',15,getdate(),5),
('qar','qpr','da',15,'01-16-17',5),
('qar','qpr','da',15,'01-15-17',5),
('qar','qpr','da',15,'01-16-17',5)
SELECT * FROM #piv
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(entryd) From #piv Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *,' + #SQL + '
From #piv
Pivot (max(time) For [entryd] in (' + #SQL + ') ) p'
Exec(#SQL);
You can create a dynamic pivot. A good example is available on this link.
Good Luck !
DECLARE #cols VARCHAR(max),#sql VARCHAR(max)
SELECT #cols=ISNULL(#cols+',[','[')+ CONVERT(VARCHAR,a.EntryDate,101)+']'
FROM mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
GROUP BY a.time
SET #sql='
SELECT * FROM (
SELECT a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) AS EntryDate,b.actualtime
FROM mpcp a, DailyData b
WHERE a.UserID=1 and a.MpCpId=b.MpCpId and where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
) AS t
PIVOT (MAX(actualtime) FOR EntryDate IN ('+#cols+') )'
EXEC(#sql)

What is the T-SQL syntax to exclude a duplicate column in the output when joining 2 tables?

I am using SQL Server 2014 and I have the following T-SQL query which joins 2 tables:
SELECT a.*, b.* FROM TEMP a
INNER JOIN Extras b ON b.ResaID = a.ResaID
I would like to pull ALL the columns from TEMP and all the columns from "Extras" with the exception of the ResaID column as it is already included in a.* in the above query. Basically, I want to pull a.* + b.* (excluding b.ResaID).
I know I can write the query in the form:
Select a.*, b.column2, b.column3,...
but since b.* has got around 40 columns, is there a way to write the query in a more simplified way to exclude b.ResaID, rather than specify each of the columns in the "Extras" table?
Unfortunately, there is no such syntax. You could either use asterisks (*) and just ignore the duplicated column in your code, or explicitly list the columns you need.
You should create a view and select the columns you need from that view. Here is a script that will generate that view for you:
DECLARE #table1 nvarchar(20) = 'temp'
DECLARE #table1key nvarchar(20) = 'ResaID'
DECLARE #table2 nvarchar(20) = 'Extras'
DECLARE #table2key nvarchar(20) = 'ResaID'
DECLARE #viewname varchar(20) = 'v_myview'
DECLARE #sql varchar(max) = ''
SELECT #sql += '], a.[' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
SELECT #sql += '], b.[' + column_name
FROM
(
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
EXCEPT
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
) x
SELECT
#sql = 'CREATE view ' +#viewname+ ' as SELECT '
+ STUFF(#sql, 1, 3, '') + '] FROM ['
+#table1+ '] a JOIN ['+ #table2
+'] b ON ' + 'a.' + #table1key + '=b.' + #table2key
EXEC(#sql)
You can simply solve this using a dynamic sql query.
DECLARE #V_SQL AS NVARCHAR(2000)='' --variable to store dynamic query
,#V_TAB1 AS NVARCHAR(200)='TEMP' --First Table
,#V_TAB2 AS NVARCHAR(200)='Extras' --Second Table
,#V_CONDITION AS NVARCHAR(2000)='A.ResaID = B.ResaID' --Conditions
SELECT #V_SQL = STUFF(
( SELECT ', '+TCOL_NAME
FROM
( SELECT 'A.'+S.NAME AS TCOL_NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1
UNION ALL
SELECT 'B.'+S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB2
AND S.NAME NOT IN (SELECT S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1)
) D
FOR XML PATH('')
),1,2,'')
EXECUTE ('SELECT '+#V_SQL+'
FROM '+#V_TAB1+' AS A
INNER JOIN '+#V_TAB2+' AS B ON '+#V_CONDITION+' ')

Resources