SQL 2005 SMO - find referencing table - sql-server

I need to change some primary keys from non-clustered to clustered but I can't drop the constraint because it is referenced from other foreign keys.
How can I find the tables that reference a primary key in the parent table as part of a foreign relation without looping through all tables in the DB? I need to disable the constraints on those, change the PK and re-enable.
Update:
I do not want to use plain SQL to do this but SMO only.
Marc, I know about ForeignKeys by I need something like:
table.PrimaryKey.ForeignKeys (i.e. which tables are referencing my table's primary key)
I just want to avoid looping through all the tables in the database and check the ForeignKeys property on each and every one of them to see if any of them reference my table.(not scalable)

Ok I think I found it.
table.Columns[0].EnumForeignKeys()
or directly
table.EnumForeignKeys()
I was expecting a property instead of a function. I am pretty sure behind the scenes it does what cmsjr suggested.

Using SMO, you could do this:
using Microsoft.SqlServer.Management.Smo;
Server localServer = new Server("your server name");
Database dasecoDB = localServer.Databases["your database name"];
Table table = dasecoDB.Tables["your table name"];
foreach(ForeignKey fk in table.ForeignKeys)
{
Console.WriteLine("Foreign key {0} references table {1} and key {2}", fk.Name, fk.ReferencedTable, fk.ReferencedKey);
}
Marc

This query should work, and could be executed using Database.ExecuteWithResults
Select fk.Table_Name from
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
where PK.Table_Name = 'SomeTable'
e.g.
SqlConnection sqlConnection =
new SqlConnection(#"Integrated Security=SSPI; Data Source=SomeInstance");
Server server = new Server(serverConnection);
Database db = server.Databases["somedatabase"];
DataSet ds = db.ExecuteWithResults(thesqlabove);

You could use the INFORMATION_SCHEMA Views.
INFORMATION_SCHEMA.TABLE_CONSTRAINTS will give you the names of the primary keys on that table.
SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = #TableName
Given the primary key names you can get the referential constraints that use those keys from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
And then the table names by querying INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE
Not SMO as such, but given the above you should be able to put together a query that will list the constraints you need to disable.

It doesn't work for me.
Consider the following relations:
Table1 --> master table;
Table2 --> slave table;
Table2.Table1_ID is a foreign key of Table1.ID
Table1.EnumForeignKeys() return null.
Instead I tried with success the DependencyWalker object. The following code list all the tables which dipend from a given collection of tables.
DependencyWalker w = new DependencyWalker(db.Parent);
DependencyTree tree = w.DiscoverDependencies(urns,false);
DependencyCollection depends = w.WalkDependencies(tree);
foreach (DependencyCollectionNode dcn in depends)
{
if (dcn.Urn.Type == "Table")
{
dcn.Urn.GetNameForType("Table");
Console.WriteLine(dcn.Urn.GetNameForType("Table"));
}
}
where "urns" is a collection of table.Urn.

You will have to travel through dependency tree.
Following is the script which use the SMO to generate Create table and insert script.
**
**ServerConnection conn = new ServerConnection( GetConnection() );
Server server = new Server( conn );
Database db = server.Databases[ mDestinationDatabase ];
// Create database script
StringBuilder dbScript = new StringBuilder();
ScriptingOptions dbCreateOptions = new ScriptingOptions();
dbCreateOptions.DriAll = true;
dbCreateOptions.NoCollation = true;
StringCollection coll = db.Script( dbCreateOptions );
foreach( string str in coll )
{
dbScript.Append( str );
dbScript.Append( Environment.NewLine );
}
sqlInsertCommands = dbScript.ToString();
// Create dependency tree
DependencyWalker w = new DependencyWalker(db.Parent);
UrnCollection urnCollection = new UrnCollection();
DataTable table = db.EnumObjects( DatabaseObjectTypes.Table );
string tableName = string.Empty;
foreach( DataRow row in table.Rows )
{
urnCollection.Add( new Urn( ( string )row[ "Urn" ] ) );
}
DependencyTree tree = w.DiscoverDependencies( urnCollection, true );
DependencyCollection depends = w.WalkDependencies(tree);
// walk through the dependency tree and for each table generate create and insert scripts
foreach (DependencyCollectionNode dcn in depends)
{
if (dcn.Urn.Type == "Table")
{
tableName = dcn.Urn.GetNameForType( "Table" );
DataTable dataTableWithData = GetTableWithData( tableName);
ArrayList columnList = new ArrayList();
foreach(DataColumn dataColumn in dataTableWithData.Columns)
{
columnList.Add( dataColumn.ColumnName );
}
sqlInsertCommands = sqlInsertCommands + Environment.NewLine + Environment.NewLine
+ GetCreateTableScript(tableName )
+ Environment.NewLine + Environment.NewLine
+ BuildInsertSQL( columnList, dataTableWithData, tableName );
}
}**
**

Related

Tool to compare DB Structure MS ACCESS vs SQL Server?

I recently migrated a MS Access database to SQL Server, mostly was imported just fine, but there are some datatype differences that I would like to find with some tool if available.
Tools I found so far compare MS Access against MS Access, or SQL Server vs SQL Server only.
At issue is that Access (or JET Red) does not have a single canonical API for working with its data-model, instead you mostly go through the OLE-DB driver or the ODBC driver. I think (but cannot confirm) that the Office Access GUI program probably its own internal API that bypasses the OLE-DB or ODBC abstractions, unfortunately the GUI program does not use specific technical terminology in things like the Table Designer (e.g. Number > Integer doesnt say if it's a 16, 32 or 64-bit integer, and Number > Replication ID is not a number at all but a Win32 GUID).
As of 2019, Microsoft has seemingly de-prioritized OLE-DB compared to the lower-level ODBC API for JET Red, but that's okay because ODBC still provides us with the necessary details for determining a database table's design.
Anyway - the good news is that you don't necessarily need a tool to compare an Access (JET Red) database with a SQL Server database because it's easy to get the ODBC table specifications yourself.
Something like this:
Dictionary<String,DataTable> jetTables = new Dictionary<String,DataTable>();
using( OleDbConnection jetConnection = new OleDbConnection( "your-access-connection-string") )
{
await jetConnection.OpenAsync().ConfigureAwait(false);
DataTable schemaTable = connection.GetOleDbSchemaTable(
OleDbSchemaGuid.Tables,
new object[] { null, null, null, "TABLE" }
);
foreach( DataRow row in schemaTable.Rows.Cast<DataRow>() )
{
String tableName = (String)row.ItemArray[2];
DataTable tableSchema = connection.GetOleDbSchemaTable(
OleDbSchemaGuid.Tables,
new object[] { null, null, tableName, "TABLE" }
);
jetTables.Add( tableName, tableSchema );
}
}
Dictionary<String,DataTable> sqlTables = new Dictionary<String,DataTable>();
using( SqlConnection sqlConnection = new SqlConnection( "your-sql-server-connection-string" ) )
{
await sqlConnection.OpenAsync().ConfigureAwait(false);
DataTable allTables = new DataTable();
using( SqlCommand cmd1 = sqlConnection.CreateCommand() )
{
cmd1.CommandText = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
using( SqlDataReader rdr1 = await cmd1.ExecuteReaderAsync.ConfigureAwait(false) )
{
allTables.Load( rdr1 );
}
}
foreach( DataRow row in allTables.Rows.Cast<DataRow>() )
{
String tableName = (String)row.ItemArray[0];
using( SqlCommand cmd2 = sqlConnection.CreateCommand() )
{
cmd2.CommandText = "SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName";
cmd2.Parameters.Add( "#tableName", SqlDbType.NVarChar ).Value = tableName;
using( SqlDataReader rdr2 = await cmd2.ExecuteReaderAsync.ConfigureAwait(false) )
{
DataTable dt = new DataTable();
dt.Load( rdr2 );
sqlTables.Add( tableName, dt );
}
}
}
}
Then compare jetTables with sqlTables as you so wish.

Reader not returning rows on check of Table Existance

I know for sure this table exists yet the reader has no rows. I expect the name of the table to come back if it exists
using (var cmd = new SqlCommand("SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'" + tableName + "') AND type in (N'U')", SqlConnection))
{
var reader = cmd.ExecuteReader();
{
using (reader)
{
if (!reader.HasRows) return false;
while (reader.Read())
tableNameFound = reader.GetString(0);
}
}
}
I ran this query straight up in Management Studio and I do get back "Cars":
SELECT name FROM sys.objects
WHERE object_id = OBJECT_ID(N'Cars') AND type in (N'U')
so maybe I shouldn't be using reader here? I don't know.
Your query is fine.
Check:
If you run this on the correct database / schema. This query will fail when running it in master for example when your table is in another database / schema;
If the parameter you enter doesn't contain spaces, etc.

sql server: a search method like google

I have this scenario :
I have a user table that has two column: Name and family.
what I want is that when user search a keyword ,for example 'name1 family1' ,all the result that have name: name1 and family: family1.
this is only one example and and my scenario is very complicated.(I want search name,lastname in one tables and phone number,address,so on related to current table).:(
is this search like google in an sql database possible? how? can I use fulltextsearch for this? how?
thank you.
Yes, it is possible. Create a view with schemabinding on your table with two columns: The ID (as PK) and a combined string of all fields you want to include in the full text search.
create view [dbo].[View_FamilyData]
with schemabinding as
select ID,
LastName+' '+FirstName+' '+Phone+' '+Address as SearchText
from dbo.YourTable
Then put a fulltext index on the the column SearchText.
Finally, play around with the various possiblities of CONTAINS,FREETEXT, CONTAINSTABLE and FREETEXTTABLE.
select T.*
from YourTable T
join View_FamilyData v
on v on T.ID = v.ID
where contains (SearchText,'Smith 12345')
private void tbautocomplete_TextChanged(object sender, EventArgs e)
{
AutoCompleteStringCollection namecollection = new AutoCompleteStringCollection();
SqlConnection con = new SqlConnection(#"Data Source=88888;Initial Catalog=contrynames;Integrated Security=True");
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT distinct(person_Firstname+''+person_Lastname) AS name FROM persondetails WHERE name Like '%'+#name+'%'";
con.Open();
SqlDataReader rea = cmd.ExecuteReader();
if (rea.HasRows == true)
{
while (rea.Read())
namecollection.Add(rea["name"].ToString());
}
rea.Close();
tbautocomplete.AutoCompleteMode = AutoCompleteMode.Suggest;
tbautocomplete.AutoCompleteSource = AutoCompleteSource.CustomSource;
tbautocomplete.AutoCompleteCustomSource = namecollection;

Strange behaviour execute SQL Server from .NET to CREATE tables or columns

I have a big SQL text file where I have a lot of SQL commands to create tables, columns, etc.
Example row(s):
IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'xcal_views') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
CREATE TABLE xcal_views (lid INT NOT NULL);
GO
IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'xcal_views_actors') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
CREATE TABLE xcal_views_actors (lid INT NOT NULL);
GO
IF NOT EXISTS ( SELECT * FROM dbo.syscolumns, dbo.sysobjects WHERE [dbo].[syscolumns].[name] = 'xlactor' AND [dbo].[sysobjects].[id] = [dbo].[syscolumns].[id] AND [dbo].[sysobjects].[id] = object_id(N'xcal_views_actors') AND OBJECTPROPERTY([dbo].[sysobjects].[id], N'IsUserTable') = 1 )
ALTER TABLE [dbo].[xcal_views_actors] ADD xlactor INT NULL;
GO
IF NOT EXISTS ( SELECT * FROM dbo.syscolumns, dbo.sysobjects WHERE [dbo].[syscolumns].[name] = 'lparentid' AND [dbo].[sysobjects].[id] = [dbo].[syscolumns].[id] AND [dbo].[sysobjects].[id] = object_id(N'xcal_views_actors') AND OBJECTPROPERTY([dbo].[sysobjects].[id], N'IsUserTable') = 1 )
ALTER TABLE [dbo].[xcal_views_actors] ADD lparentid INT NULL;
GO
IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE parent_obj = (SELECT id FROM dbo.sysobjects WHERE id = object_id(N'xcal_views_actors') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) AND OBJECTPROPERTY(id, N'IsPrimaryKey') = 1)
ALTER TABLE [dbo].[xcal_views_actors]
ADD CONSTRAINT [CT_00000501] PRIMARY KEY CLUSTERED (lid ASC);
GO
IF NOT EXISTS ( SELECT * FROM [sys].[indexes] i INNER JOIN [sys].[objects] o ON o.object_id = i.object_id AND o.name = 'xcal_views_actors' WHERE i.name = 'parent_id' )
CREATE INDEX parent_id ON xcal_views_actors (lparentid ASC)
GO
Between each command I have a GO in extra line to separate the commands.
If I run the whole patch.sql file from SQL Server Management Studio all commands are executed and works fine.
In .NET I read the whole text file, then split them with 'GO' and execute each SQL command against the database.
Now the strange thing: some of the commands don't get executed. And I can't find out why.
This is the method that does the job:
private static void patchDatabase(string connection, string sqlfile)
{
var defaultEncoding = Encoding.Default;
using (FileStream fs = File.OpenRead(sqlfile))
{
defaultEncoding = TextFileEncodingDetector.DetectTextFileEncoding(fs, defaultEncoding, 1024);
}
//Console.WriteLine(string.Format("File {0} using encoding: {1}",sqlfile, defaultEncoding));
var dbPatch = new StreamReader(sqlfile, defaultEncoding);
string sqlPatch = dbPatch.ReadToEnd();
dbPatch.Close();
string[] stringSeparators = new[] {"GO"};
string[] sqlPatches = sqlPatch.Split(stringSeparators, StringSplitOptions.None);
if (connection != null && sqlPatch.Length > 0)
{
Console.WriteLine(string.Format("Executing {0} statements from {1}", sqlPatches.Length, sqlfile));
using (var cnn = new SqlConnection(connection))
{
cnn.Open();
foreach (var sql in sqlPatches)
{
if (String.IsNullOrEmpty(sql))
continue; // Not a real sql statement, use next
using (var cmd = new SqlCommand(sql, cnn))
{
try
{
cmd.CommandTimeout = 120;
cmd.ExecuteNonQuery();
//int results = cmd.ExecuteNonQuery();
//if (results < 1)
// Console.WriteLine(String.Format("Failed:\nResult: {0}\n{1}",results, sql));
}
catch (Exception ex)
{
Console.WriteLine("Execution error!\n\n" + sql + "\n\n\n" + ex);
}
}
}
cnn.Close();
}
}
}
It looks like my function splutters...
My current textfile has around 6.000+ lines.
Any idea what I do wrong?
It looks like it wasn't a coding problem at all.
In fact I produced the following situation:
I have a database "test_db"
I create a user "test_db_user" and make him db_owner
He is added in sql server in general "security - roles" with objekt "test_db" as role db_owner
He also gets added as user in the database "test_db" - security - user.
Now it comes: I restored the database again after some tests.
The user is not anymore listed in "test_db" - security - user
but still configured as db_owner in general.
Somehow the connection works then but it don't has not full access to the db all the time. Can't really know what is going wrong.
From the management studio I ever used admin account for starting the sql batches, that is the reason it worked all the time there.
So solution: Make sure the security settings are 100% correct and then it works :S
Another problem was my stupidity :S!!! I had some create table statements without dbo. schema before the table so the different user created a different schema name for the table.
Thanks to all for the feedback.

how to overwrite repeat data in the database in a efficient way?

I use Sql server 2008 to store my data,and the table structure like that
index float not null,
type int not null,
value int not null,
and the (index,type) is unique.there are not two datas has the same index and the same type.
So when I insert the data to the table, I have to check the (index,type) pair whether in the table already, if it exists I use update statement, otherwise, I insert it directly.but I think this is not a efficient way,because:
Most of the data' index-type pair is not existed int the table.so the select operation is waste, especially the table is huge.
When I use C# or other CLR language to insert the data, I can't use batch copy or batch insert.
is there any way to overwrite the data directly without check whether it is existed in the table?
If you want to update OR insert the data, you need to use merge:
merge MyTable t using (select #index index, #type type, #value value) s on
t.index = s.index
and t.type = s.type
when not matched insert (index, type value) values (s.index, s.type, s.value)
when matched update set value = s.value;
This will look at your values and take the appropriate action.
To do this in C#, you have to use the traditional SqlClient:
SqlConnection conn = new SqlConnection("Data Source=dbserver;Initial Catalog=dbname;Integrated Security=SSPI;");
SqlCommand comm = new SqlCommand();
conn.Open();
comm.Connection = conn;
//Add in your values here
comm.Parameters.AddWithValue("#index", index);
comm.Parameters.AddWithValue("#type", type);
comm.Parameters.AddWithValue("#value", value);
comm.CommandText =
"merge MyTable t using (select #index index, #type type, #value value) s on " +
"t.index = s.index and t.type = s.type " +
"when not matched insert (index, type value) values (s.index, s.type, s.value) " +
"when matched update set value = s.value;"
comm.ExecuteNonQuery();
comm.Dispose();
conn.Close();
conn.Dispose();
You should make (index, type) into a composite primary key (aka compound key).
This would ensure that the table can only even have unique pairs of these (I am assuming the table does not have a primary key already).
If the table does have a primary key, you can add a UNIQUE constraint onto those columns with similar effect.
Once defined, this means that any attempt to insert a duplicate pair would fail.
Other answers recommend constraints. Creating constraints just means you will be executing insert statements that trigger errors. The next step (after having created the constraints) is something like INSERT ON DUPLICATE KEY UPDATE, which apparently does have an Sql Server equivalent.

Resources