MS SQL find multiple columns in a table - sql-server

Normally, I use this code to find a column in my table:
Use MyDatabase
Go
IF EXISTS(SELECT * FROM sys.columns
WHERE Name in ( N'String1')
AND Object_ID = Object_ID(N'dbo.Table1'))
BEGIN
Print 'Column String1 exists in Table1'
END
ELSE
BEGIN
Print 'Column String1 does not exist in Table1'
END;
We're updating the table and adding 56 new columns. How do I go about that without duplicating the code 56 times? It's just a quick error check to do after importing the new columns. Another thing I'd like to do is just print out the result only if the column isn't found.
Thanks!
Matt

You can declare a table containing all the 56 column names you want to check and then left join it against the management view:
DECLARE #cols TABLE
(
column_name varchar(500)
)
INSERT INTO #cols
VALUES ('col1'),('col2'),('col3')
IF EXISTS (SELECT *
FROM #cols c
LEFT JOIN INFORMATION_SCHEMA.COLUMNS col ON col.COLUMN_NAME = c.column_name
AND col.TABLE_NAME = 'Table1'
WHERE col.TABLE_NAME IS NULL)
BEGIN
PRINT 'Some columns are missing'
SELECT c.column_name
FROM #cols c
LEFT JOIN INFORMATION_SCHEMA.COLUMNS col ON col.COLUMN_NAME = c.column_name
AND col.TABLE_NAME = 'Table1'
WHERE col.TABLE_NAME IS NULL
END
INFORMATION_SCHEMA.COLUMNS is functionally the same as sys.columns. I prefer it for being cleaner and have more info than sys.columns.

Related

Is it possible to select alle columns from table, except one (eg. ID)?

I am using a third party application which has an absurd number of columns per table. When I select data, often I need all columns except the ID. Or all columns except ID and DateCreated.
Using the sys.columns it's possible to find out which columns are available in a table. How can I use this information to create statements? What would be the best way to do this?
This script will select all columns for any table except the primary key column of the table and the column names DateCreated.
SELECT
'SELECT '+
SUBSTRING(LIST,1,LEN(LIST)-1)
+' FROM [Person].[Address]'
FROM
(
SELECT
'['+COL.COLUMN_NAME+'],'
FROM INFORMATION_SCHEMA.COLUMNS COL
LEFT JOIN
(
SELECT
CON.CONSTRAINT_TYPE,
USG.TABLE_SCHEMA,
USG.TABLE_NAME,
USG.COLUMN_NAME,
CON.CONSTRAINT_NAME,
USG.TABLE_CATALOG
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE USG
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS CON
ON USG.CONSTRAINT_NAME = CON.CONSTRAINT_NAME
)Q
ON COL.TABLE_SCHEMA = Q.TABLE_SCHEMA
AND COL.TABLE_NAME = Q.TABLE_NAME
AND COL.TABLE_CATALOG = Q.TABLE_CATALOG
AND COL.COLUMN_NAME = Q.COLUMN_NAME
WHERE COL.TABLE_SCHEMA ='Person'
AND COL.TABLE_NAME = 'Address'
AND
(
Q.CONSTRAINT_TYPE <> 'PRIMARY KEY'
OR
COL.COLUMN_NAME <> 'DateCreated'
)
FOR XML PATH(''))L(LIST)
Replace the string Person with your schema and Address With you table name
try using dynamic Query:
DECLARE #Names VARCHAR(MAX)
SELECT #Names = COALESCE(#Names + ', ', '') + Column_Name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name='Table1'
And Column_Name!='ID'
DECLARE #Query VARCHAR(MAX)
SELECT #Query = 'SELECT '+ #Names + ' INTO Table2 FROM Table1'
exec (#Query)
SELECT * from Table2

Compare tables on Linked Servers SQL dynamically

I have to compare table (SIB$) to get unmatched records on two different LINKED SERVERS (LATESTDUMP, OLDDUMP) that are identical. I have already tried to create a dynamic query. Can some one please help me with following:
1) Is there a way where I dont have to pass the column names to the code and the code dynamically gets the column names and use it as the column list to compare.
So all I have to do is pass to the stored proc the two table names
Code i have worked on:
DECLARE #sql nvarchar(max) = ' ((SELECT * FROM LATESTDUMP...SIB$) t1 FULL
OUTER JOIN (SELECT * FROM OLDDUMP...SIB$) t2
ON t1.id = t2.id
WHERE
t1.id IS NULL OR
t2.id IS NULL)'
SELECT #sql += ' or t1.' + quotename(column_name) + ' <> t2.' +
quotename(column_name) from information_schema.columns where table_name =
'SIB$'
Looks like you might want to look at the MERGE statement that's been available since SQL 2008.
https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
Here's some code to get you started. It pulls out a primary key (assumes one column primary keys, which may or may not be a valid assumption for you), and grabs a comma-delimited string list of the remaining columns.
From here you can use split-string to build a sql string that joins the two same-named tables from the hard-coded linked servers on the primary key, comparing each of the columns for difference, and then execute the dynamic SQL. I've included some test scaffolding so you can work it through:
DECLARE #tableName sysname;
SET #tableName = 'some table'
-- Validate parameter
IF #tableName IS NULL OR NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #tableName AND TABLE_TYPE = 'BASE TABLE')
BEGIN
RAISERROR ('Invalid table name specified', 16, 1);
RETURN;
END;
-- Validate table has a primary key
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = #tableName)
BEGIN
RAISERROR ('Specified table does not have a primary key', 16, 1);
RETURN;
END;
-- Get info about the Primary Key columns
DECLARE #pkcolName sysname;
SELECT #pkcolName = c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.COLUMNS c ON kcu.TABLE_NAME = c.TABLE_NAME AND kcu.COLUMN_NAME = c.COLUMN_NAME
WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.TABLE_NAME = #tableName AND kcu.ORDINAL_POSITION = 1
-- Grab the names of all the remaining columns
DECLARE #nonKeyColumns nvarchar(MAX);
SELECT #nonKeyColumns = STUFF ( ( SELECT N'], [' + c.name
FROM sys.columns c
WHERE object_id = (select top 1 object_id FROM sys.objects where name = #tableName)
AND c.name <> #pkcolName
ORDER BY c.column_id
FOR XML PATH('')), 1, 2, '') + ']';
SELECT #pkcolName
SELECT #nonKeyColumns

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+' ')

How do I validate a variable against multple database tables

Does anyone know how to check a a variable against all database table with columns storing the same type of information? I have a poorly designed database that stores ssn in over 60 tables within one database. some of the variations of columns in the various tables include:
app_ssn
ca_ssn
cand_ssn
crl_ssn
cu_ssn
emtaddr_ssn
re_ssn
sfcart_ssn
sfordr_ssn
socsecno
ssn
Ssn
SSN
I want to create a stored procedure that will accept a value and check it against every table that has 'ssn' in the name.Does anyone have idea as to how to do this?
-- I assume that table/column names don't need to be surrounded by square braces. You may want to save matches in a table - I just select them. I also assume ssn is a char.
alter proc proc1
#search1 varchar(500)
as
begin
set nocount on
declare #strsql varchar(500)
declare #curtable sysname
declare #prevtable sysname
declare #column sysname
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
order by table_schema+'.'+table_name +column_name
-- make sure that at least one column has ssn in the column name
if #curtable is not null
begin
while (1=1)
begin
set #strsql = 'select * from ' +#curtable +' where '+''''+#search1+''''+ ' = '+#column
print #strsql
-- any matches for passed in ssn will match here...
exec (#strsql)
set #prevtable = #curtable+#column
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
and table_schema+'.'+table_name +column_name> #prevtable
order by table_schema+'.'+table_name +column_name
-- when we run out of columns that contain ssn we are done...
if ##ROWCOUNT = 0
break
end
end
end
What you will need to do is some research. But here is where you can start;
SELECT tbl.NAME AS TableName
,cl.NAME AS ColumnName
,IDENTITY(INT, 1, 1) AS ID
INTO #ColumnsToLoop
FROM sys.tables tbl
JOIN sys.columns cl ON cl.object_id = tbl.object_id
This will give you the table / column relation then you can simply build a dynamic SQL string based on each row in the query above (basically loop it) and use EXEC or sp_execsql. So basically;
DECLARE #Loop int = (select min(ID) From #ColumnsToLoop),#MX int = (Select MAX(ID) From #ColumnsToLoop)
WHILE(#Loop<=#MX)
BEGIN
DECLARE #SQL nvarchar(MAX) = 'SQL String'
//Construct the dynamic SQL String
EXEC(#SQL);
SET #Loop += 1
END
Perhaps I went a little too crazy with this one, but let me know. I thought it would best the primary key of the search results with the table name so you could join it to your tables. I also managed to do it without a single cursor or loop.
DECLARE #SSN VARCHAR(25) = '%99%',
#SQL VARCHAR(MAX);
WITH CTE_PrimaryKeys
AS
(
SELECT TABLE_CATALOG,
TABLE_SCHEMA,
TABLE_NAME,
column_name
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE D
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
),
CTE_Columns
AS
(
SELECT A.*,
CONCAT(A.TABLE_CATALOG,'.',A.TABLE_SCHEMA,'.',A.TABLE_NAME) AS FullTableName,
CASE WHEN B.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IsPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS A
LEFT JOIN CTE_PrimaryKeys B
ON A.TABLE_CATALOG = B.TABLE_CATALOG
AND A.TABLE_SCHEMA = B.TABLE_SCHEMA
AND A.TABLE_NAME = B.TABLE_NAME
AND A.COLUMN_NAME = B.COLUMN_NAME
),
CTE_Select
AS
(
SELECT
'SELECT ' +
--This returns the pk_col casted as Varchar and the table name in another columns
STUFF((SELECT ',CAST(' + COLUMN_NAME + ' AS VARCHAR(MAX)) AS pk_col,''' + B.TABLE_NAME + ''' AS Table_Name'
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
AND B.IsPrimaryKey = 1
FOR XML PATH ('')),1,1,'')
+ ' FROM ' + fullTableName +
--This is where I list the columns where LIKE desired SSN
' WHERE ' +
STUFF((SELECT COLUMN_NAME + ' LIKE ''' + #SSN + ''' OR '
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
--This is where I filter so I only get desired columns
AND (
--Uncomment the Collate if your database is case sensitive
COLUMN_NAME /*COLLATE SQL_Latin1_General_CP1_CI_AS*/ LIKE '%ssn%'
--list your column Names that don't have ssn in them
--OR COLUMN_NAME IN ('col1','col2')
)
FOR XML PATH ('')),1,0,'') AS Selects
FROM CTE_Columns A
GROUP BY A.FullTableName,A.TABLE_NAME
)
--Unioning them all together and getting rid of last trailing "OR "
SELECT #SQL = COALESCE(#sql,'') + SUBSTRING(selects,1,LEN(selects) - 3) + ' UNION ALL ' + CHAR(13) --new line for easier debugging
FROM CTE_Select
WHERE selects IS NOT NULL
--Look at your code
SELECT SUBSTRING(#sql,1,LEN(#sql) - 11)

Is it possible to Add column to multiple table simultaneously?

I am using SQL Server. I want to add a single column named [DateCreated] to multiple tables. Is it possible that with a single statement I could add this column to all the tables in my database?
I stumble upon an answer by Joe Steffaneli in which he suggested a query which in turn returns rows consisting Alter table statements.
Query is as follows :
select 'alter table ' + quotename(s.name) + '.' + quotename(t.name) + ' add [DateModified] datetime'
from sys.columns c
inner join sys.tables t
on c.object_id = t.object_id
inner join sys.schemas s
on t.schema_id = s.schema_id
left join sys.columns c2
on t.object_id = c2.object_id
and c2.name = 'DateModified'
where c.name = 'DateCreated'
and t.type = 'U'
and c2.column_id is null /* DateModified column does not already exist */
Is there any way that I can execute returned rows? Sorry for English.
You probably need something like this. Check that the script does what you want before running it (adds a non null column with a default value of getdate())!
DECLARE #Dynsql nvarchar(max)
SET #Dynsql = ''
SELECT #Dynsql = #Dynsql + '
alter table ' + QUOTENAME(SCHEMA_NAME(schema_id))+ '.' + QUOTENAME(name) +
' add [DateCreated] datetime not null default getdate()'
FROM sys.tables
WHERE type='U' and object_id NOT IN (select object_id from sys.columns where name='DateCreated')
EXEC (#Dynsql)
You can use this query
I use the where clause in this query (it is optional) to find all tables that have ID and add the new field to them.
you can change where clause for example find all tables that have BusinessId column and add new filed or remove where clause and add for all tables
DECLARE #SQL varchar(max)
SELECT #SQL= STUFF((SELECT ';' + 'ALTER TABLE ' + t.TABLE_SCHEMA+'.' +t.TABLE_NAME+ ' ADD newfield nvarchar(max)'
from information_schema.tables t
inner join information_schema.columns c on c.table_name = t.table_name
and c.table_schema = t.table_schema
where c.column_name = 'id'
and t.table_schema not in ('information_schema', 'pg_catalog')
and t.table_type = 'BASE TABLE'
FOR XML PATH('')),1,1,'')
EXEC (#SQL)
This stored procedure works fine
USE databaseName;
exec sp_msforeachtable 'alter table ? Add [DateCreated] datetime not null default getdate()';
declare #i int
set #i=1
while(#i<45)
begin
declare #sql varchar(200)
with TempTable as (select Row_number() over(order by stdID) as RowNo,* from SomeTable)
select #sql= 'alter table Table'+(select Name from TempTable where RowNo=#i)+' add NewColumn int'
exec (#sql)
set #i=#i+1
end
No, there is no single statement that will add a column to all the tables in your database.
Next time please tag your question with the RDBMS you're using. If there were a way, we wouldn't be able to give you the command without knowing which database system you are using.

Resources