Get dynamic SQL for altering primary keys - sql-server

Based on the examples here, here and here, I tried to build dynamic SQL so that I don't have to manually do this at some point. However, I am facing a challenge when the PK is a combination of more than one column. This code is creating scripts separately for each additional column that is part of PK. I would like to have it something with number of columns in the suffix if it has more than one column that should be part of PK - _12, _123, etc.
ALTER TABLE <Schema_Name>.<Table_Name>
DROP CONSTRAINT <constraint_name>
ALTER TABLE <Schema_Name>.<Table_Name>
ADD CONSTRAINT PK_<Schema_Name>_<Table_Name>_12 PRIMARY KEY (<Column1>,<Column2>)
Code built so far:
SELECT
STUFF((
select ';' + 'ALTER TABLE ' +
s.name + '.' +
t.name + ' DROP CONSTRAINT ' +
i.name + ';' +
'ALTER TABLE ' + s.name + '.' + t.name +
' ADD CONSTRAINT ' + 'PK__' + s.name + '__' + t.name + '__' +
--tc.name,
string_agg(ic.key_ordinal, ',') +
' PRIMARY KEY (' + tc.name + ')'
from
sys.schemas s
inner join sys.tables t on s.schema_id=t.schema_id
inner join sys.indexes i on t.object_id=i.object_id
inner join sys.index_columns ic on i.object_id=ic.object_id
and i.index_id=ic.index_id
inner join sys.columns tc on ic.object_id=tc.object_id
and ic.column_id=tc.column_id
where i.is_primary_key=1
GROUP BY s.name, t.name, i.name, tc.name, ic.key_ordinal
order by t.name, ic.key_ordinal
FOR XML PATH('')),1,1,'') + ';'
;

Related

Renaming the column names in batch using SQL Server 2012

I found this useful query to rename all my tables, indexes and constrains but I just figured out it didn't rename the columns.
SELECT 'exec sp_rename ' + '''' + NAME + '''' + ', ' + '''' + replace(NAME, 'Tb', 'Tabela') + ''''
FROM sysObjects
WHERE
NAME LIKE 'Tb%'
I know there's syscolumns but I'm not sure how to use in this case.
Question: How can I get the same result of this query but for columns instead of tables?
I appreciate your help in this. I'm using SQL Server 2012. Thanks.
You have to do a little more work:
SELECT 'exec sp_rename ' + '''' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + '.' + QUOTENAME(c.name) + '''' + ', ' + '''' + replace(c.name, 'Col', 'Column') + ''', ''COLUMN'''
FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE c.name LIKE 'Col%'
Since you are renaming column, you must specify that third argument to sp_rename is COLUMN. You must also construct three part name in the form of [schema].[table name].[current column name] to point to the correct column.

Subquery table based on sys.tables

I'm trying to retrieve all primary keys iin a fact table and then count the number of records in that fact table grouped by that id, however so far i can only seem to get all the primary keys and its talbe. i guess i need to make some kind of subquery
SELECT
tab1.name AS [FactTable],
col1.name AS [PrimaryKey]
FROM sys.indexes ind1
INNER JOIN sys.tables tab1
ON tab1.object_id = ind1.object_id
INNER JOIN sys.schemas sch1
ON tab1.schema_id = sch1.schema_id
INNER JOIN sys.columns col1
ON col1.object_id = tab1.object_id AND col1.name like '%Id'
WHERE tab1.name like 'Fact%' AND ind1.is_primary_key = 1
sample output
primaryKey countRecordsGroupedByPrimaryKey
2 4000
3 8343
4 203
1 4023
I going out on a limb and guessing that you want to count the number of each dimensionPK used in your fact table. If a fact table has references to two different dimensions, you need two different statements to count the usage by that dimension.
For the query below, provide your fact table name and schema and it should generate a count statement joining your fact and dim and grouping by the join keys.
- If a fact table has two different FK relationships, you'll get two different statements.
- If a table uses a composite PK, both key columns will be included in the join
This is complicated and I don't have any tables with multiple FKs to test it on, so please let me know if it does what you want.
DECLARE #NameOfTableWithFKs sysname = 'your fact table name',
#SchemaOfTableWithFKs sysname = 'dbo';
WITH JoinColumns
AS (SELECT QUOTENAME(OBJECT_SCHEMA_NAME(parent.object_id)) + '.' + QUOTENAME(OBJECT_NAME(parent.object_id)) AS ParentTableName,
QUOTENAME(OBJECT_SCHEMA_NAME(referenced.object_id)) + '.' + QUOTENAME(OBJECT_NAME(referenced.object_id)) AS ReferencedTableName,
QUOTENAME(OBJECT_NAME(parent.object_id)) + '.' + QUOTENAME(parent.name) + ' = ' + QUOTENAME(OBJECT_NAME(referenced.object_id)) + '.'
+ QUOTENAME(referenced.name) AS JoinColumn,
QUOTENAME(OBJECT_NAME(referenced.object_id)) + '.' + QUOTENAME(referenced.name) AS GroupingColumn
FROM sys.foreign_key_columns AS fkc
INNER JOIN sys.columns AS parent
ON parent.object_id = fkc.parent_object_id
AND parent.column_id = fkc.parent_column_id
INNER JOIN sys.columns AS referenced
ON referenced.object_id = fkc.referenced_object_id
AND referenced.column_id = fkc.referenced_column_id
WHERE OBJECT_NAME(parent.object_id) = #NameOfTableWithFKs
AND OBJECT_SCHEMA_NAME(parent.object_id) = #SchemaOfTableWithFKs
),
JoinTables
AS (SELECT QUOTENAME(OBJECT_SCHEMA_NAME(tbl.object_id)) + '.' + QUOTENAME(OBJECT_NAME(tbl.object_id)) AS ParentTableName,
QUOTENAME(OBJECT_SCHEMA_NAME(rtbl.object_id)) + '.' + QUOTENAME(OBJECT_NAME(rtbl.object_id)) AS ReferencedTableName
FROM sys.tables AS tbl
INNER JOIN sys.foreign_keys AS cstr
ON cstr.parent_object_id = tbl.object_id
INNER JOIN sys.tables AS rtbl
ON rtbl.object_id = cstr.referenced_object_id
WHERE OBJECT_NAME(tbl.object_id) = #NameOfTableWithFKs
AND OBJECT_SCHEMA_NAME(tbl.object_id) = #SchemaOfTableWithFKs
)
SELECT 'SELECT Count(*)' + ( SELECT ', ' + JC.GroupingColumn
FROM JoinColumns AS JC
WHERE JC.ParentTableName = jt.ParentTableName
AND JC.ReferencedTableName = jt.ReferencedTableName
FOR XML PATH('')
) + ' FROM ' + JT.ParentTableName + ' INNER JOIN ' + JT.ReferencedTableName + ' ON'
+ SUBSTRING(( SELECT ' AND ' + JC.JoinColumn
FROM JoinColumns AS JC
WHERE JC.ParentTableName = JT.ParentTableName
AND JC.ReferencedTableName = JT.ReferencedTableName
FOR XML PATH('')
), 5, 8000
) + ' GROUP BY ' + SUBSTRING(( SELECT ', ' + JC.GroupingColumn
FROM JoinColumns AS JC
WHERE JC.ParentTableName = JT.ParentTableName
AND JC.ReferencedTableName = JT.ReferencedTableName
FOR XML PATH('')
), 2, 8000
)
FROM JoinTables AS JT;
If I understand the question correctly you are wanting to count all the rows in some tables based on some criteria. Not really sure why you care about the primary key portion since by definition a primary key must be unique so this could still be simplified to not check for primary key but whatever.
I did remove the join to sys.columns because why does it matter the name of the column unless you want only those table named Fact% and has a column named %Id.
This should get you pretty close as I understand it.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select TableName = ''' + tab1.name + ''', NumRows = count(*) from ' + QUOTENAME(sch1.name) + '.' + QUOTENAME(tab1.name) + ' UNION ALL '
FROM sys.indexes ind1
INNER JOIN sys.tables tab1
ON tab1.object_id = ind1.object_id
INNER JOIN sys.schemas sch1
ON tab1.schema_id = sch1.schema_id
--INNER JOIN sys.columns col1
-- ON col1.object_id = tab1.object_id AND col1.name like '%Id'
WHERE tab1.name like 'Fact%'
AND ind1.is_primary_key = 1
select #SQL = LEFT(#SQL, LEN(#SQL) - 10) + ' ORDER BY TableName'
select #SQL --uncomment the exec line below once you are comfortable that the dynamic sql is what you want.
--exec sp_executesql #SQL

Scripting foreign keys from sys tables -- struggling with keys that have multiple columns

Hi Everyone and thanks in advance.
I've been trying to script foreign keys from my existing database and am having problems with keys that reference multiple columns.
The script I'm using is below:
SELECT
'FOREIGN_KEY' as KeyType,'ALTER TABLE ' + OBJECT_NAME(f.parent_object_id) +
' WITH CHECK ADD CONSTRAINT ' + '[' + OBJECT_NAME(f.object_id) +
']' + ' FOREIGN KEY ([' + col_name(fc.parent_object_id,fc.parent_column_id) +
']) REFERENCES ' + OBJECT_NAME(f.referenced_object_id) +
' ([' + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) + ']) ALTER TABLE ' +
OBJECT_NAME(f.parent_object_id) + ' CHECK CONSTRAINT [' +
OBJECT_NAME(f.object_id) + ']' as result
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) = #TableName
This will return the desired foreign keys but as stated above I'm having problems with the following results:
ALTER TABLE [dbo].[TreatmentPlanItems] WITH CHECK ADD CONSTRAINT [FK_TreatmentPlanItems_TPNumber] FOREIGN KEY([PatientCode]) REFERENCES [dbo].[TreatmentPlans] ([PatientCode]) ALTER TABLE [dbo].[TreatmentPlanItems] CHECK CONSTRAINT [FK_TreatmentPlanItems_TPNumber]
ALTER TABLE [dbo].[TreatmentPlanItems] WITH CHECK ADD CONSTRAINT [FK_TreatmentPlanItems_TPNumber] FOREIGN KEY([TPNumber]) REFERENCES [dbo].[TreatmentPlans] ([TPNumber]) ALTER TABLE [dbo].[TreatmentPlanItems] CHECK CONSTRAINT [FK_TreatmentPlanItems_TPNumber]
The output I need should be the following:
ALTER TABLE [dbo].[TreatmentPlanItems] WITH CHECK ADD CONSTRAINT [FK_TreatmentPlanItems_TPNumber] FOREIGN KEY([Patientcode],[TPNumber]) REFERENCES [dbo].[TreatmentPlans] ([Patientcode],[TPNumber]) ALTER TABLE [dbo].[TreatmentPlanItems] CHECK CONSTRAINT [FK_TreatmentPlanItems_TPNumber]
Any help would be much appreciated.
Thanks
Below is a code sample, I hope you can adopt it to your needs:
declare #tName sysname;
set #tName = 'dbo.TableName';
with vPKColumns as (
select c.object_id, c.column_id, c.name, ixc.key_ordinal
from sys.key_constraints pk
join sys.index_columns ixc on ixc.object_id = pk.parent_object_id and ixc.index_id = pk.unique_index_id
join sys.columns c on c.object_id = pk.parent_object_id and c.column_id = ixc.column_id
where pk.parent_object_id = object_id(#tName) and pk.type = 'PK'
),
vFKs as (
select
quotename(fk.name) as name,
fk.parent_object_id,
fk.object_id,
case
when fk.delete_referential_action_desc collate database_default = 'NO_ACTION' then NULL
else ' on delete ' + replace(lower(FK.delete_referential_action_desc) collate database_default, '_', ' ')
end as onDelete,
case
when fk.update_referential_action_desc collate database_default = 'NO_ACTION' then NULL
else ' on update ' + replace(lower(FK.update_referential_action_desc) collate database_default, '_', ' ')
end as onUpdate
from sys.foreign_keys fk
where exists (
select 1
from sys.foreign_key_columns fkc
join vPKColumns pkc on pkc.object_id = fkc.referenced_object_id and pkc.column_id = fkc.referenced_column_id
where fkc.referenced_object_id = fk.referenced_object_id
)
)
select
'alter table ' + quotename(s.name) + '.' + quotename(t.name) + '
with nocheck add constraint ' + fk.name + ' foreign key (' + stuff(
(select ', ' + quotename(c.name)
from sys.foreign_key_columns fkc
join sys.columns c on c.object_id = fkc.parent_object_id and c.object_id = fkc.parent_object_id and c.column_id = fkc.parent_column_id
where fkc.constraint_object_id = fk.object_id
order by fkc.constraint_column_id
for xml path(''), type).value('text()[1]','nvarchar(max)'),1,2,'') + ') references ' + #tName + ' (' +
stuff(
(select ', ' + quotename(pkc.name)
from vPKColumns pkc
order by pkc.key_ordinal
for xml path(''), type).value('text()[1]','nvarchar(max)'),1,2,'') + ')' +
isnull('
' + onDelete, '') +
isnull('
' + onUpdate, '') + ';
GO
alter table ' + quotename(s.name) + '.' + quotename(t.name) + '
check constraint ' + fk.name + ';
GO'
from vFKs fk
join sys.tables t on t.object_id = fk.parent_object_id
join sys.schemas s on s.schema_id = t.schema_id
order by s.name, t.name, fk.name;

Using single select query get the Minimum,Maximum values of Identity Columns of all the tables in a Database

Using a single select query, I need to get the Minimum and Maximum values of Identity Columns along with the other columns specified in the query below, for all tables in a given database.
This is what I've been able to code to get a list of tables and their identity columns:
Select so.name as TableName
, sic.name as ColumnName
, i.Rows Count_NumberOfRecords
, IDENT_CURRENT(so.name)+IDENT_INCR(so.name) as NextSeedValue
from sys.identity_columns sic
inner join sys.objects so on sic.object_id = so.object_id
inner join sys.sysindexes I ON So.OBJECT_ID = I.ID
Where so.type_desc = 'USER_TABLE' and last_value is not null and indid IN (0,1);
The query needs to get these extra columns:
MaximumValue (IdentityColumn) and MinimumValue (IdentityColumn) for each table.
You should be able to use something along these lines:
DECLARE #cmd NVARCHAR(max);
SET #cmd = '';
SELECT #cmd = #cmd + CASE WHEN (#cmd = '') THEN '' ELSE ' UNION ALL ' END + 'SELECT ''' +
QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ''' AS TableName, ''' +
QUOTENAME(c.name) + ''' AS ColumnName, MAX(' + QUOTENAME(c.name) + ') AS MaxID, MIN(' +
QUOTENAME(c.name) + ') AS MinID, COALESCE(IDENT_CURRENT(''' + QUOTENAME(s.name) + '.' +
QUOTENAME(t.name) + '''),0) + COALESCE(IDENT_INCR(''' + QUOTENAME(s.name) + '.' +
QUOTENAME(t.name) + '''),0) AS NextValue FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.schemas s on t.schema_id = s.schema_id
WHERE c.is_identity = 1
SELECT #cmd; /* Shows the dynamic query generated, not necessary */
EXEC sp_executesql #cmd;
The query uses dynamic SQL to construct a UNION query that gathers the Table Name, Column Name, and Min and Max ID values currently in every table that has an IDENTITY field.
You could quite easily modify this to show the columns in the format you want, along with the other columns you mention in your question.
I've edited the query above to include the "NextValue" field, however I agree with #AaronBertrand in that this value is of little use, since in a busy system it will most certainly be wrong immediately (or shortly thereafter) once the query executes.

copy FOREIGN KEY constraints from one DB to other

I have a local db which has FOREIGN KEY constraints.
The live version of this websites DB, does not have any of these FOREIGN KEY constraints.
How can I "copy/paste", import/export ONLY the FOREIGN KEY constraints from one db to the other?
I do NOT want to copy any data, only the constraints.
Thanks
You could use this script I found at http://www.siusic.com/wphchen/how-to-script-out-all-the-foreign-keys-of-a-table-106.html. Replace tablename1 and tablename2 with the list of tables you wish to get the foreign keys for.
select 'ALTER TABLE '+object_name(a.parent_object_id)+
' ADD CONSTRAINT '+ a.name +
' FOREIGN KEY (' + c.name + ') REFERENCES ' +
object_name(b.referenced_object_id) +
' (' + d.name + ')'
from sys.foreign_keys a
join sys.foreign_key_columns b
on a.object_id=b.constraint_object_id
join sys.columns c
on b.parent_column_id = c.column_id
and a.parent_object_id=c.object_id
join sys.columns d
on b.referenced_column_id = d.column_id
and a.referenced_object_id = d.object_id
where object_name(b.referenced_object_id) in
('tablename1','tablename2')
order by c.name
I needed to do something similar, where I needed the same foreign keys on multiple servers, except some had already been added. So I added a "IF NOT EXISTS" check to the beginning of the creation statements:
SELECT N'
IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(''' + QUOTENAME(fk.name) + ''')
AND parent_object_id = OBJECT_ID(''' + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) +''')
)
BEGIN
ALTER TABLE '
+ QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name)
+ ' ADD CONSTRAINT ' + QUOTENAME(fk.name)
+ ' FOREIGN KEY (' + STUFF((SELECT ',' + QUOTENAME(c.name)
-- get all the columns in the constraint table
FROM sys.columns AS c
INNER JOIN sys.foreign_key_columns AS fkc
ON fkc.parent_column_id = c.column_id
AND fkc.parent_object_id = c.[object_id]
WHERE fkc.constraint_object_id = fk.[object_id]
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'')
+ ') REFERENCES ' + QUOTENAME(rs.name) + '.' + QUOTENAME(rt.name)
+ '(' + STUFF((SELECT ',' + QUOTENAME(c.name)
-- get all the referenced columns
FROM sys.columns AS c
INNER JOIN sys.foreign_key_columns AS fkc
ON fkc.referenced_column_id = c.column_id
AND fkc.referenced_object_id = c.[object_id]
WHERE fkc.constraint_object_id = fk.[object_id]
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'') + ')
END'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS rt -- referenced table
ON fk.referenced_object_id = rt.[object_id]
INNER JOIN sys.schemas AS rs
ON rt.[schema_id] = rs.[schema_id]
INNER JOIN sys.tables AS ct -- constraint table
ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs
ON ct.[schema_id] = cs.[schema_id]
WHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0;
If you do not want the "IF NOT EXISTS" checks (they really shouldn't matter), just delete the top 5 lines and add "SELECT N' " right before the "BEGIN", like this:
SELECT N'BEGIN
ALTER TABLE '
+ QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name)
-- I found the core of this query online somewhere and I've been modifying it for some time. Credit to them for putting most of it together...

Resources