Creating table from structure of another table with no data - sql

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

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

INNER JOIN with ON All columns except one Column

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.

One view from two tables with identical column names

We have two tables that we need to merge into a singular view. Normally I'd individually select columns to avoid this issue, however in this case the two tables are a combined 800 columns.
The only identical columns are the identifier columns. Unfortunately these cannot be changed as they are used by a 3rd party tool to sync table
Table A
GUID
Name
Address
...
Table B
GUID
Cell
Fax
Home2
...
Are good examples, just assume each table has 400 odd columns.
Obviously the traditional
SELECT a.*, b.* from table_a a, table_b a where a.guid = b.guid
Fails miserably. Is there any easy way to create the view without having to list out 799 individual column names? I was thinking perhaps a one off function to create the view but so far I'm hitting a wall.
You can use dynamic sql as a solution.
CREATE TABLE test1 (id INT, col1 NVARCHAR(50), col2 NVARCHAR(50))
GO
CREATE TABLE test2(id INT, col1 NVARCHAR(50), col2 NVARCHAR(50))
GO
DECLARE #sql NVARCHAR(max) = ''
; WITH cte AS (
SELECT
CASE WHEN TABLE_NAME = 'test1' THEN TABLE_NAME + '.' + COLUMN_NAME + ' AS ' + + COLUMN_NAME + 't1' ELSE TABLE_NAME + '.' + COLUMN_NAME + ' AS ' + + COLUMN_NAME + 't2' END AS a, 1 AS ID
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN ('test1', 'test2')
)
SELECT #sql =
'CREATE VIEW myview as
select ' + (
SELECT
STUFF(
(
SELECT ', '+ [A]
FROM cte
WHERE ID = results.ID
FOR XML PATH(''), TYPE
).value('(./text())[1]','VARCHAR(MAX)')
,1,2,''
) AS NameValues
FROM cte results
GROUP BY ID
) + ' from test1 join test2 on test1.id = test2.id'
PRINT #sql
--EXEC (#sql)
The result is
CREATE VIEW myview
AS
SELECT test1.id AS idt1 ,
test1.col1 AS col1t1 ,
test1.col2 AS col2t1 ,
test2.id AS idt2 ,
test2.col1 AS col1t2 ,
test2.col2 AS col2t2
FROM test1
JOIN test2 ON test1.id = test2.id

SQL - Get rows from all tables where a common column is null

I need to find all of the rows in any table where the create/update date are null
There are over 500 tables so doing something like the following is not feasible.
SELECT *
FROM
(SELECT
'tableA' AS `table`,
IF(COUNT(`column_a`), NULL, 'column_a') AS `column`
FROM tableA
UNION ALL
SELECT
'tableB' AS `table`,
IF(COUNT(`column_b`), NULL, 'column_b') AS `column`
FROM tableB
UNION ALL
-- etc.
) t
WHERE
`column` IS NOT NULL
I figured I could use INFORMATION_SCHEMA somehow but I am having difficulty with that.
I generally do this by using sys.tables to populate a list of the tables into a temp table, then compose dynamic SQL around that list.
select quotename( schemas.name ) + '.' + quotename(tables.name) as tbl
into #work
from sys.tables
join sys.schemas on tables.schema_id = schemas.schema_id
select
'select ''' + tbl + ''' as [table] where exists ( select * from ' + tbl + ' where column_a is null )' as sqlcmd,
'select ''' + tbl + ''' as [table], * from ' + tbl + ' where createdate is null ' as sqlcmd
from #work
drop table #work
Once you have the list you can execute it manually in SSMS or iterate through it using a cursor to execut each statement. The quotation marks can give you a headache, though. Dynamic SQL isn't for the faint of heart.

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.