INNER JOIN with ON All columns except one Column - sql

I have 2 tables(Table1 and Table2). Both tables schema are exactly the same and both might have duplicated set of records except IDs since ID is auto generated.
I would like to get the common set of records but with ID to follow as Table1's ID. So, I query using Inner join. It works as I expected.
SELECT Table1.ID, Table1.Param1, Table1.Param2, Table1.Param3
INTO #Common
FROM Table1
INNER JOIN Table2 ON Table1.Param1 = Table2.Param1
AND Table1.Param2 = Table2.Param2
AND Table1.Param3 = Table2.Param3
However, in actual usage, the total number of parameters in both tables will be around 100. So, the total number of comparison inside ON clause will increase up to 100.
How can I do inner join by excluding one column instead of comparing all columns in ON clause?
By removing ID column from both tables and doing intersect also no possible since I still want to extract Table1 ID for other purpose.
I can achieve the common of 2 table by removing ID and compare those 2 table.
However, that still do not serve my requirement, since I need to get Table1 ID for those common data.
SELECT * INTO #TemporaryTable1 FROM Table1
ALTER TABLE #TemporaryTable1 DROP COLUMN ID
SELECT * INTO #TemporaryTable2 FROM Table2
ALTER TABLE #TemporaryTable2 DROP COLUMN ID
SELECT * INTO #Common FROM (SELECT * FROM #TemporaryTable1 INTERSECT SELECT * FROM #TemporaryTable2) data
SELECT * FROM #Common

If i understood your problem correctly i guess you could generate dynamically the query you want to use using the following code :
DECLARE #SQL nvarchar(max) = 'SELECT ',
#TBL1 nvarchar(50) = 'data',
#TBL2 nvarchar(50) = 'data1',
#EXCLUDEDCOLUMNS nvarchar(100)= 'ID,col1'
-- column selection
SELECT #sql += #tbl1 + '.' + COLUMN_NAME + ' ,
'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TBL1
-- from clause and remove last ,
set #SQL = LEFT(#sql,LEN(#sql) - 5)
SET #sql += '
FROM ' + #TBL1 + ' INNER JOIN
' + #TBL2 + '
ON '
-- define the on clause
SELECt #SQL += #tbl1 + '.' + COLUMN_NAME + ' = '+ #tbl2 + '.' + COLUMN_NAME +',
'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TBL1
AND COLUMN_NAME not in (#EXCLUDEDCOLUMNS)
--remove last ,
set #SQL = LEFT(#sql,LEN(#sql) - 3)
--SELECt #SQL
EXEC SP_EXECUTESQL #sql
Before you execute make sure the #sql is properly generated. choose the columns you want to exclude from your on clause using the #EXCLUDEDCOLUMNS parameter.

Related

Update columns in multiple tables by names pulled from a temporary table

I have a temp table where various table names and connected column names are stored. If I were to run a simple SELECT on it the results would look something like this:
----------------
TableName | ColumnName
------------------
Users | RoleId
Tables | OwnerId
Chairs | MakerId
etc...
I'm looking for a way to set mentioned column values in the connected tables to NULL.
I know how to do it via a CURSOR or a WHILE loop by processing each row individually but I'm trying to eliminate these performance hoarders from my stored procedures.
Is there any way to build a join by table names from the TableName column to the actual tables to then set connected ColumnName column values to NULL ?
Check this Script-
IF OBJECT_ID('SampleTable') IS NOT NULL
DROP TABLE SampleTable
CREATE TABLE SampleTable
(
Table_Name VARCHAR(50) NOT NULL,
Column_Name VARCHAR(50) NOT NULL
)
GO
INSERT INTO SampleTable
VALUES
('Users','RoleId'),('Tables','OwnerId'),('Chairs','MakerId') --Give your Combo here
GO
--Check this scripts
SELECT 'UPDATE ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(S1.TABLE_NAME) +
' SET ' + QUOTENAME(S1.COLUMN_NAME) + ' = NULL ; '
AS [Dynamic_Scripts]
FROM SampleTable S JOIN INFORMATION_SCHEMA.COLUMNS S1 ON s.Table_Name=s1.Table_Name and s.Column_Name=s1.Column_Name
--Check this scripts (multiple column single script; 1 table 'n' column - 1 update query)
SELECT 'UPDATE ' + CONCAT('[',TABLE_SCHEMA,'].[',S1.TABLE_NAME,'] SET ') + STRING_AGG(CONCAT('[',S1.COLUMN_NAME,']=NULL'),',') + ' ; ' AS [Dynamic_Scripts]
FROM SampleTable S JOIN INFORMATION_SCHEMA.COLUMNS S1 ON s.Table_Name=s1.Table_Name and s.Column_Name=s1.Column_Name
GROUP BY CONCAT('[',TABLE_SCHEMA,'].[',S1.TABLE_NAME,'] SET ')
Try this,
IF OBJECT_ID('SampleTable') IS NOT NULL
DROP TABLE SampleTable
CREATE TABLE SampleTable
(
Table_Name VARCHAR(50) NOT NULL,
Column_Name VARCHAR(50) NOT NULL
)
GO
INSERT INTO SampleTable
VALUES
('Users','RoleId'),('Tables','OwnerId'),('Chairs','MakerId')
,('Users','Appid'),('Tables','Column') --Give your Combo here
GO
declare #Sql nvarchar(1000)=''
;with CTE as
(
select QUOTENAME(a.Table_Name)Table_Name
,stuff((select ','+QUOTENAME(Column_Name),'=null'
from SampleTable B
where a.Table_Name=b.Table_Name for xml path('') ),1,1,'')UpdateCol
from SampleTable A
group by a.Table_Name
)
select #Sql=coalesce(#Sql+char(13)+char(10)+SingleUpdate,SingleUpdate)
from
(
select CONCAT('Update ',Table_Name,' ','SET ',UpdateCol)SingleUpdate
from cte
)t4
print #Sql
select #Sql
Execute sp_executeSql #Sql

Compare two tables in SQL Server

I have two tables in my SQL Server, both tables has the same columns. I need find the differences between this tables.
The tables (all fields are in nvarchar format):
following the tables columns, i need make an SQL query to indentify this conditions:
table1.uf = table2.uf,
table1.municipio = table2.municipio,
table1.marca_modelo = table2.marca_modelo,
table1.ano_fabricacao = table2.ano_fabricacao
table1.qtd_veiculos != table2.qtd_veiculos
and
`Unique lines in table1`
I've already tried make this query (bellow) but doesn't work.
Select *
from Table1 J left join
Table2 M on J.uf = M.uf
and J.municipio = M.municipio
and J.marca_modelo = M.marca_modelo
and J.ano_fabricacao = M.ano_fabricacao
and J.qtd_veiculos != M.qtd_veiculos
Can you help me with that question?
I expected to receive the following result:
The fields in red show the differences between tables and in the last line, in Table1 not have any correlation in Table2.
I apologize for the lack of information, this is my first topic here in the forum. but thanks for the help!
Here is a script I use to compare two tables. Perhaps you can modify this for yourself by throwing the results into a temp table for further analysis.
You could change the '*'s to list only those columns you want to compare.
-- Thisc script compares any two tables.
-- Enter the two tables names to compare in the first two lines.
DECLARE #table1 NVARCHAR(80)= 'my_first_table_to_compare'
DECLARE #table2 NVARCHAR(80)= 'my_second_table_to_compare'
DECLARE #sql NVARCHAR (2000)
SET #sql = '
SELECT ''' + #table1 + ''' AS table_name,* FROM
(
SELECT * FROM ' + #table1 + '
EXCEPT
SELECT * FROM ' + #table2 + '
) x
UNION
SELECT ''' + #table2 + ''' AS table_name,* FROM
(
SELECT * FROM ' + #table2 + '
EXCEPT
SELECT * FROM ' + #table1 + '
) y
'
EXEC sp_executesql #stmt = #sql
I don't fully understand your question, so this is a [wild] guess. You want to find:
Rows present on both tables but they differ on qtd_veiculos.
Rows on table1 that are not present in table2.
Rows on table2 that are not present in table1.
If this is the question, the query should be:
select j.*, m.*
from table1 j
outer join table2 m on j.uf = m.uf
and j.municipio = m.municipio
and j.marca_modelo = m.marca_modelo
and j.ano_fabricacao = m.ano_fabricacao
where j.qtd_veiculos <> m.qtd_veiculos

query not retrieving values returned from split string

I generate comma seperated string and add single quite to each numbers
Here is how i do it
DECLARE #IDs NVARCHAR(max)
SELECT #IDs = COALESCE(#IDs +',', '') + ''''
+ Cast([mynos] AS NVARCHAR(255)) + ''''
FROM mytable
WHERE id = 22
If i print variable #IDs then i get below output
'78888','3333','1222'
When i use same variable in this query then query doesnt return any value
SELECT *
FROM table1
WHERE ids IN ( #IDs )
How to fix this?
It doesn't work as your query is effectively doing this:
SELECT *
FROM TABLE
WHERE Ids IN ('''78888'',''3333',''1222''');
Which would also be equivalent to:
SELECT *
FROM TABLE
WHERE Ids = '''78888'',''3333',''1222''';
If you want to do the query as you have done, you'll need to split your delomited data out again. As you're using SQL Server 2012, you can't make use of STRING_SPLIT, so you'll need to a different one; such as Jeff Moden's DelimitedSplit8K. Then you can do:
SELECT *
FROM TABLE
WHERE IDs IN (SELECT items
FROM dbo.DelimitedSplit8K (#IDs,','));
However, why are you not simply doing...
SELECT *
FROM TABLE T
WHERE EXISTS (SELECT 1
FROM myTable mT
WHERE mT.Id = 22
AND mT.myNos = T.Ids);
You can use dynamic query #id is string variable not multi-value argument
DECLARE #IDs nVARCHAR(MAX)
SELECT #IDs = COALESCE(#IDs +',' ,'') + '''' + CAST([myNos] AS nVARCHAR(255)) + ''''
FROM myTable WHERE Id = 22
DECLARE #query nVARCHAR(MAX)
SET #query = "Select * from table1 where Ids in ("+#IDs+")"
EXECUTE sp_executesql #query
I tried below and it worked
select * from table1 where id in (select mynos from mytable where id = 22)
Thanks to #Larnu for giving me idea

Pivoting help needed

Please help me with this. I am totally stuck. I have coders block or something.
I have the following table
ID Name Cost Included
---- ---------- ------- ----------
1 Package1 10.00 Yes
2 Package2 20.00 No
3 Package3 20.00 Yes
I would like to crosstab this information, to display like the following example,there will be more columns in the table.
Type Package1 Package2 Package3
----- ------------ ----------- ----------
Name Package1 Package2 Package3
Cost 10.00 20.00 30.00
Included Yes No Yes
It seems to me that you are trying to build a product comparison list. If this is true, you might unpivot the table first and then join individual records together.
The 'transponded' part unpivots the columns. All columns must be of compatible types or converted to one. I choose varchar(100). transponded returns table with three columns, ID from ProductInfo, Type as column name and Value as value of corresponding column.
Select part joins together info on as many product as demanded by adding another left join transponded tn on t1.Type = tnType and tn.ID = #parametern. This part seems as a hassle, but when I tried to do this part with pivot I failed to get column in proper order - pivot sorted names in Type. It would however demand dynamic sql generation. This solution is fixed providing that you add enough joins for maximum products you wish to compare at once. I belive it would not be over 5.
=1, =2 and =3 should be replaced by parameters. The query should be hosted in stored procedure.
; with transponded as
(
select ID, Type, Value
from
(
select ID,
Name,
cast (Cost as varchar(100)) Cost,
cast (case when Included = 1 then 'Yes' else 'No' end as varchar(100)) Included
from ProductInfo
) p
unpivot (Value for Type in (Name, Cost, Included) ) a
)
select t1.Type,
t1.Value Product1,
t2.Value Product2,
t3.Value Product3
from transponded t1
left join transponded t2
on t1.Type = t2.Type
and t2.id = 2
left join transponded t3
on t1.Type = t3.Type
and t3.id = 3
where t1.id = 1
In short, transpond one record at time and join to another transponded record by Type column.
Oh, and here is a Sql Fiddle playground.
There is no easy way to do this, as the pivot will need to be aggregated by column. Given that adding columns to the input table would cause a maintenance issue where these values will not be presented to the output until the code is changed wherever it is used, I'd say you're probably best doing it once with a stored procedure, which will dynamically generate the output you're looking for based on the schema of the input table.
I have demonstrated how this can be done, using the data you have supplied. This data is stored in a temp table (not #temp, because the stored proc won't work with temporary tables), populated thus:
CREATE TABLE temp (
_key int,
package_name varchar(50),
cost float,
included bit
)
INSERT INTO temp VALUES(1,'Package1', 10.00, 1)
INSERT INTO temp VALUES(2,'Package2', 20.00, 0)
INSERT INTO temp VALUES(3,'Package3', 20.00, 1)
The stored procedure retrieves a list of values based on the #pivot_field parameter, and uses these values as a column list to be inserted after the "Type" field. It then unions the pivot field and all other fields together to generate the rows, pivoting one column at a time. The procedure is as follows:
CREATE PROCEDURE usp_get_pivot (#table_name nvarchar(255), #pivot_field nvarchar(255)) AS
BEGIN
CREATE TABLE #temp (val nvarchar(max))
DECLARE #sql NVARCHAR(MAX), #cols NVARCHAR(MAX), #col NVARCHAR(255)
SET #sql = 'SELECT DISTINCT ' + #pivot_field + ' FROM ' + #table_name
INSERT INTO #temp EXEC sp_executesql #sql;
SET #cols = (SELECT '[' + val + '],' FROM #temp FOR XML PATH(''))
SET #cols = SUBSTRING(#cols, 1, LEN(#cols)-1)
SET #SQL = N'SELECT ''' + #pivot_field + ''' as [type], *
FROM (SELECT ' + #pivot_field + ', ' + #pivot_field + ' as ' + #pivot_field + '1 FROM ' + #table_name + ') AS source_table
PIVOT (max(' + #pivot_field + '1) FOR ' + #pivot_field + ' IN (' + #cols + ')) AS pivot_table'
DECLARE csr CURSOR FOR
SELECT c.name FROM sys.columns c, sys.objects o
WHERE c.object_id = o.object_id AND o.name = #table_name
AND c.name <> #pivot_field
ORDER BY column_id
OPEN csr
FETCH NEXT FROM csr INTO #col
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = #sql + ' UNION ALL
SELECT ''' + #col + ''' as [type], *
FROM (SELECT ' + #pivot_field + ', CAST(' + #col + ' AS VARCHAR) AS ' + #col + ' FROM ' + #table_name + ') AS source_table
PIVOT (max(' + #col + ') FOR ' + #pivot_field + ' IN (' + #cols + ')) AS pivot_table'
FETCH NEXT FROM csr INTO #col
END
CLOSE csr
DEALLOCATE csr
DROP TABLE #temp
EXEC sp_executesql #sql
END
You should be able to simply copy and paste the procedure into management studio, create the data is shown above and execute the procedure with:
EXEC usp_get_pivot 'temp', 'package_name'
If number of packages is not static there is no option for you I think. PIVOT clause can produce only static/defined number of columns.
You may do some table-to-table rewriting using multiple statements - but still you have to face with static number of columns.
But you may set it to for example to 10 and then display up to 10 packages, having NULL-s in rest of columns if there are less packages.
You may also use dynamic SQL to have dynamic number of columns - but it will be a headache.
If you're going to export this data to Excel - do not pivot it at SQL - do a transposition in Excel (it's under "paste special").
Basically what i have at this stage is the following.
SELECT [Type],
MAX(Beginner) AS [Beginner],
MAX(Intermediate) AS [Intermediate],
MAX(Advanced) AS [Advanced]
FROM
(
SELECT
'Name' AS TYPE,
CASE WHEN Name='Beginner' THEN Name END AS [Beginner],
CASE WHEN Name='Intermediate' THEN Name END AS [Intermediate],
CASE WHEN Name='Advanced' THEN Name END AS [Advanced]
FROM Administration.Package
UNION ALL
SELECT
'Price' AS TYPE,
CASE WHEN Name='Beginner' THEN CAST(Price AS VARCHAR) END AS [Beginner],
CASE WHEN Name='Intermediate' THEN CAST(Price AS VARCHAR) END AS [Intermediate],
CASE WHEN Name='Advanced' THEN CAST(Price AS VARCHAR) END AS [Advanced]
FROM Administration.Package
)A
GROUP BY [Type]
But it does not feel right to have the union for each and every column.

Creating table from structure of another table with no data

How can this be achieved in SQL (MS SQL)
Thanks
OK: let me bit clear what am trying to acheive is generating dynamic select statement with EXCEPT clause.
eg: select col1,col2,col3 from #table
except
select col1,col2,col2 from #table
so my resultset will be always different based on #table
futher down i want to use this #table in CTE
with filteredData
as
(
select col1, col2 from temptable --(created above)
)
below is the code so far:
DECLARE #TABLENAME VARCHAR(200) = 'SERVICE_DELIVERY_LOCATION';
DECLARE #COLCOUNT INT ;
DECLARE #TEMPCOLNAME VARCHAR(500) ;
DECLARE #SELECTCOLUMNS VARCHAR(5000)='';
DECLARE #EXCEPTSTATEMENT VARCHAR(5000)='' ;
---------------------------------------------------------------------------------------------------
--CASE: GET COMMON_COLUMNS COLUMNS OF SOURCE AND DESTINATION TABLE
---------------------------------------------------------------------------------------------------
DECLARE #COMMON_COLUMNS TABLE( COLUMN_NAME VARCHAR(500))
INSERT INTO #COMMON_COLUMNS
SELECT SOURCE.COLUMN_NAME FROM SCD_SOURCE.INFORMATION_SCHEMA.COLUMNS SOURCE
INNER JOIN SCD_DESTI.INFORMATION_SCHEMA.COLUMNS DESTI ON SOURCE.COLUMN_NAME = DESTI.COLUMN_NAME
---------------------------------------------------------------------------------------------------
--CASE: GET PK COLUMNS OF SOURCE TO MAP TO DESTINATION IN CASE WHERE NEED TO DO UPDATES
---------------------------------------------------------------------------------------------------
DECLARE #PK_COLUMNS TABLE ( PK_COLUMN VARCHAR(500))
INSERT INTO #PK_COLUMNS
SELECT KCU.COLUMN_NAME
FROM SCD_SOURCE.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
JOIN SCD_SOURCE.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
AND KCU.TABLE_SCHEMA = TC.TABLE_SCHEMA
AND KCU.TABLE_NAME = TC.TABLE_NAME
AND KCU.COLUMN_NAME != 'CREATE_DATA_CONTAINER_ID'
WHERE TC.CONSTRAINT_TYPE IN ('PRIMARY KEY')
SELECT #COLCOUNT = COUNT(*) FROM #COMMON_COLUMNS
WHILE ( #COLCOUNT != 0 )
BEGIN
SET #TEMPCOLNAME = (SELECT TOP 1 COLUMN_NAME FROM #COMMON_COLUMNS)
SET #SELECTCOLUMNS = #SELECTCOLUMNS + #TEMPCOLNAME + ', '
DELETE FROM #COMMON_COLUMNS WHERE COLUMN_NAME = #TEMPCOLNAME
SELECT #COLCOUNT = COUNT(*) FROM #COMMON_COLUMNS
END
SET #SELECTCOLUMNS = SUBSTRING(#SELECTCOLUMNS, 1, LEN(#SELECTCOLUMNS) - 1)
SET #EXCEPTSTATEMENT = 'SELECT ' + #SELECTCOLUMNS + ' FROM SCD_SOURCE.DBO.' + #TABLENAME + ' EXCEPT SELECT ' + #SELECTCOLUMNS + ' FROM SCD_DESTI.DBO.' + #TABLENAME
EXEC(#EXCEPTSTATEMENT)
want the resultset of last line into temptable
thanks
SELECT TOP 0 *
INTO NewTable
FROM OriginalTable
This will copy the structure, but won't copy constraints etc.
If you want everything, best thing to do is just generate the script from SSMS and change the table/constraint/index names.
Edit:
Re: "want the resultset of last line into temptable"
You could change the last 2 lines to:
SET #EXCEPTSTATEMENT = 'SELECT * INTO MyNewTable FROM (SELECT ' + #SELECTCOLUMNS + ' FROM SCD_SOURCE.DBO.' + #TABLENAME + ' EXCEPT SELECT ' + #SELECTCOLUMNS + ' FROM SCD_DESTI.DBO.' + #TABLENAME + ') x'
EXECUTE(#EXCEPTSTATEMENT)
This would put the resultset into a "real" table; alterneratively you'd have to use a global temporary table (just change "MyTable" to "##MyTable"). It wouldn't work with a local temporary table ("#MyTable") as the scope of that would be within the EXECUTE statement i.e. after the EXECUTE, you couldn't have code that then queried the local temporary table as it won't exist in that scope. Hence, it would need to be a real table, or a global temporary table (which would be accessible outside of the current scope)/
Open your managment studio, right click to your table and create to -> New Query window. This will generate the query for creating exact copy of your table with indexes constraints etc...
I've used this answer / script before.
SELECT *
INTO NewTable
FROM OriginalTable
WHERE 1 = 0