Check for constraint on table - sql-server

If I check for a column's existance as follows, before adding it, how would I do the equivalent for a UNIQUE constraint?
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[Table]') AND [Name]='Column')
ALTER TABLE [Table]
ADD
[Column] varchar(500)

select * from sys.objects where type = 'uq' and parent_object_id = OBJECT_ID('[Table]')

Something like this for a constraint FK_myTable (SQL 2000):
if not exists ( select *
from sysconstraints sc
inner join sysobjects tbl on sc.id = tbl.id
inner join sysobjects con on sc.constid = con.id
where tbl.name = 'myTable' and con.name = 'FK_myTable' )

Related

Using column names from another table to define the name of a new table

I'm looking to create a new table name as the name of the column in a previous table but it can't seem to recognize the #table_name_2 variable. I'm trying to do the following;
DECLARE #table_name_2 varchar(MAX)
SET #table_name_2 =
(select
col.name as column_name
from sys.tables as tab
inner join sys.columns as col
on tab.object_id = col.object_id
left join sys.types as t
on col.user_type_id = t.user_type_id
where schema_name(tab.schema_id) = 'Staging' and tab.name = 'QACalculator' and col.column_id = 2)
CREATE TABLE #table_name_2
(
MI_KEY NOT NULL
ISSUE NULL
)

How to find foreign keys to tables which contain an arbitrary column name

I would like to find a set of tables which contain:
a foreign key constraint to a specific table, and
another arbitrary column name.
I'm using:
EXEC sp_fkeys 'MyTable'
This returns all of tables with foreign key constraints to 'MyTable', but I would like to do additional filtering.
As I have a particularly large list of resulting tables to work with in my database, I would like to filter the FKTABLE_NAME by tables that contain an arbitrary column name, for example CreatedOn, which is not necessarily itself the linked column.
Something like this will look for tables that contain a FK to "yourtablename" where the referencing table has a column "yourcolumnname"
SELECT
OBJECT_NAME(f.parent_object_id) TableName,
COL_NAME(fc.parent_object_id,fc.parent_column_id) ColName
,*
FROM
sys.foreign_keys AS f
INNER JOIN
sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
INNER JOIN
sys.tables t
ON t.OBJECT_ID = fc.referenced_object_id
WHERE
OBJECT_NAME (f.referenced_object_id) = 'yourtablename'
AND EXISTS (SELECT 1 FROM sys.columns c WHERE c.name='yourcolumnname' AND c.object_id=f.parent_object_id)
You'll get a list of columns in the below. You can then select distinct table name and apply any other filters you need.
--build your temp table with output of SP_FKeys
CREATE TABLE #Temp (
PKTABLE_QUALIFIER VARCHAR(100),
PKTABLE_OWNER VARCHAR(100),
PKTABLE_NAME VARCHAR(100),
PKCOLUMN_NAME VARCHAR(100),
FKTABLE_QUALIFIER VARCHAR(100),
FKTABLE_OWNER VARCHAR(100),
FKTABLE_NAME VARCHAR(100),
FKCOLUMN_NAME VARCHAR(100),
KEY_SEQ INT,
UPDATE_RULE int,
DELETE_RULE int,
FK_NAME VARCHAR(100),
PK_NAME VARCHAR(100),
DEFERRABILITY int
)
--Populate it
INSERT INTO #Temp
EXEC sp_fkeys #pktable_name = N'Department'
,#pktable_owner = N'HumanResources';
--Now, join to systables and syscolums
SELECT * FROM #Temp TEMP JOIN sys.tables ST ON TEMP.FKTABLE_NAME = ST.name
JOIN sys.columns SC ON ST.object_id = SC.object_id
WHERE SC.name = 'CreatedOn' --enter your column name here

Collation issue while join query in MVC5 and SQL Server 2012

When I join two tables I got a collation issue that is
System.Data.SqlClient.SqlException: Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AI" in the equal to operation.
Then I set the collation in my db using the following code
ALTER DATABASE [CAM] COLLATE SQL_Latin1_General_CP1_CI_AS;
ALTER TABLE CAM_Users
ALTER COLUMN [EmployeeCode] VARCHAR(50)
COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL;
But I still get the same error.
My join query is this:
List<DTOUserManagement> users = (from CAMuser in _unitOfWorkAsync.RepositoryAsync<CAM_Users>().Queryable()
join QRuser in _unitOfWorkAsync.RepositoryAsync<CAM_V_EmployeeMaster>().Queryable() on CAMuser.EmployeeCode equals QRuser.EmployeeCode into t
from t1 in t.DefaultIfEmpty()
join CAMDomain in _unitOfWorkAsync.RepositoryAsync<CAM_Domain>().Queryable() on CAMuser.DomainID equals CAMDomain.DomainID into t2
from t3 in t2.DefaultIfEmpty()
where CAMuser.IsActive
select new DTOUserManagement
{
TransactUserCode = CAMuser.TransactUserCode,
EmployeeCode = CAMuser.EmployeeCode,
EmployeeName = t1.EmployeeName,
Email = t1.EMail,
DomainID = CAMuser.DomainID,
DomainName = t3.DomainName,
IsActive = CAMuser.IsActive,
AssignedRole = CAMuser.AssignedRoles
}).ToList();
How can I solve this?
Please reply anybody
The problem here is that the COLLATION must match on joining columns.
There are two ways to fix this. First you could change the collation on each column. Or second, you can change the collation at execution time. Here's an example of the second approach:
Sample Data
/* T1 and T2 are identical tables in structre and content, except
* for the collation.
*/
DECLARE #T1 TABLE
(
ID VARCHAR(3) COLLATE SQL_Latin1_General_CP1_CI_AS
)
;
DECLARE #T2 TABLE
(
ID VARCHAR(3) COLLATE Latin1_General_CI_AI
)
;
INSERT INTO #T1
(
ID
)
VALUES
('x'),
('y'),
('z')
;
INSERT INTO #T2
(
ID
)
VALUES
('x'),
('y'),
('z')
;
Anti Pattern - Will not Work
/* This query will failed with the error:
* Cannot resolve the collation conflict between "Latin1_General_CI_AI" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID
;
Corrected - No Error
/* Success.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID COLLATE Latin1_General_CI_AI
;
You need to check your tables that are involved in these JOINs here to verify that all columns involved have the same collation.
You can do this with this SQL query:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
t.name IN ('CAM_Users', 'CAM_Domain') -- add any further tables to check
AND ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
ORDER BY
t.name, c.name
If there are columns that do not match the database default collation, you need to change those to be the same as all other columns. Once all the string columns in those tables are the same collation, then your joins should work.
Update: use this query to find those tables & columns that do not have the current default database collation:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
AND c.collation_name <> 'SQL_Latin1_General_CP1_CI_AS'
ORDER BY
t.name, c.name

How to get list of child tables for a database table?

I have to write a delete script to delete rows form a database table. However the table has a lot of children tables (foreign keys) and those children tables have children tables too.
There are foreign keys for all relationships and I'd like to use this info to get the list of tables where I'll have to deletes, in the correct order (leaf tables first and then up the dependency graph).
How can I get the list of child tables for a given table in the correct order?
try this on your database, this script will only give you the graph for one table at a time. I assume you have an Employee table but you would have to change line 2 to check a specific table of your database:
DECLARE #masterTableName varchar(1000)
SET #masterTableName = 'Employee'
DECLARE #ScannedTables TABLE( Level int, Name varchar(1000) collate Latin1_General_CI_AS )
DECLARE #currentTableCount INT
DECLARE #previousTableCount INT
DECLARE #level INT
SET #currentTableCount = 0
SET #previousTableCount = -1
SET #level = 0
INSERT INTO #ScannedTables VALUES ( #level, #masterTableName )
WHILE #previousTableCount <> #currentTableCount
BEGIN
SET #previousTableCount = #currentTableCount
INSERT INTO #ScannedTables
SELECT DISTINCT
#level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name
WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN ( SELECT Name FROM #ScannedTables WHERE Level = #level )
AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN ( SELECT Name FROM #ScannedTables )
SET #level = #level + 1
SELECT #currentTableCount = COUNT(*) FROM #ScannedTables
END
SELECT * FROM #ScannedTables
There is no simple generic answer for this, since tables can recursively depend on other tables including self relationships, etc. Your result could be more than simple tree.
Your best way should depend on your db model: if you have tree tables connected, then delete your data from third table first, than second, than third.
...or disable constraints, delete data, enable constraints.
...or change foreign keys to DELETE CASCADE.
It depends on your data model.
This article gives a good idea of how to do what you're asking.
EDIT: I've modified the original query given in the link to:
Make the script schema aware
Correct the bug noted in the
comments below
Not sure why the editor is doing such a poor job of formatting the code block.
with Fkeys as (
select distinct
OnTable = onTableSchema.name + '.' + OnTable.name
,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name
from
sysforeignkeys fk
inner join sys.objects onTable
on fk.fkeyid = onTable.object_id
inner join sys.objects againstTable
on fk.rkeyid = againstTable.object_id
inner join sys.schemas onTableSchema
on onTable.schema_id = onTableSchema.schema_id
inner join sys.schemas againstTableSchema
on againstTable.schema_id = againstTableSchema.schema_id
where 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name
)
,MyData as (
select
OnTable = s.name + '.' + o.name
,AgainstTable = FKeys.againstTable
from
sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join FKeys
on s.name + '.' + o.name = FKeys.onTable
left join Fkeys fk2
on s.name + '.' + o.name = fk2.AgainstTable
and fk2.OnTable = Fkeys.AgainstTable
where 1=1
and o.type = 'U'
and o.name not like 'sys%'
and fk2.OnTable is null
)
,MyRecursion as (
-- base case
select
TableName = OnTable
,Lvl = 1
from
MyData
where 1=1
and AgainstTable is null
-- recursive case
union all select
TableName = OnTable
,Lvl = r.Lvl + 1
from
MyData d
inner join MyRecursion r
on d.AgainstTable = r.TableName
)
select
Lvl = max(Lvl)
,TableName
,strSql = 'delete from [' + tablename + ']'
from
MyRecursion
group by
TableName
order by
1 desc
,2 desc

Is there a way to get the fields names of a table created in a function / stored procedure?

when I need the columns of an existing table I use the query:
SELECT c.[name]
FROM
(SELECT * from syscolumns) c
INNER JOIN
(SELECT [id] from sysobjects where name= 'tableName') o on c.[id]=o.[id]
I need the fields of a table that I create during runTime:
select
a.ID,
b.lName,
b.fName
into #T
from
a
inner join
b on a.id=b.id
.
select * from #T_columns
will result a table with 3 rows:
id
lName
fName
How can I do it?
Thanks
When you create a temp table, it will be in tempdb. You can look it up like this:
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#T|_%' ESCAPE '|'
If you do a SELECT * FROM INFORMATION_SCHEMA.TABLES in tempdb, you'll see the temp table name you use (#T) actually has a number of underscores appended to it followed by a unique identifier. So you won't find it it you just search where table_name = '#T'.
So that's why you have to use a LIKE as I've shown above. This will match on "#T_" followed by any other other characters.
Try this
SELECT sc.NAME
FROM
tempdb..SYSOBJECTS so JOIN
tempdb..SYSCOLUMNS sc ON sc.id = so.id
WHERE so.NAME LIKE '#T%'

Resources