SQL 2008 - Foreign key constraints in the INFORMATION_SCHEMA view - c#

I am writing a c# unit test that validates string properties for an ORM class against the target database, always SQL 2008, and the class that the data maps to.
Checking that a specified foreign key is valid in the DB is easy:
static private bool ConstraintExsits(string table, string column, ConstraintType constraintType)
{
string constraintTypeWhereClause;
switch (constraintType)
{
case ConstraintType.PrimaryKey:
constraintTypeWhereClause = "PRIMARY KEY";
break;
case ConstraintType.ForeignKey:
constraintTypeWhereClause = "FOREIGN KEY";
break;
default:
throw new ArgumentOutOfRangeException("constraintType");
}
var cmd = new SqlCommand(
#"SELECT a.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS a
JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE b on a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
WHERE a.TABLE_NAME = #table AND b.COLUMN_NAME = #column AND a.CONSTRAINT_TYPE = '" + constraintTypeWhereClause + "'",
Connection);
cmd.Parameters.AddWithValue("#table", table.Trim('[').Trim(']'));
cmd.Parameters.AddWithValue("#column", column.Trim('[').Trim(']'));
return !string.IsNullOrEmpty((string)cmd.ExecuteScalar());
}
Now take the following Foreign Key Relationships:
My question: How do I query the relationship from the 'Primary/Unique Key Base Table' and 'Primary/Unique Key Columns' side? I cannot see these referenced in the INFORMATION_SCHEMA views.
Thanks
J

This is the SQL that I was after!
SELECT
FK_Table = FK.TABLE_NAME,
FK_Column = CU.COLUMN_NAME,
PK_Table = PK.TABLE_NAME,
PK_Column = PT.COLUMN_NAME,
Constraint_Name = C.CONSTRAINT_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
INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN
(
SELECT
i1.TABLE_NAME, i2.COLUMN_NAME
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT
ON PT.TABLE_NAME = PK.TABLE_NAME

Jaimie's answer fails to correctly return all the foreign keys if the referred table (the table that the foreign key is looking against) has a unique key because it uses the UNIQUE_CONSTRAINT_NAME column. I suggest:
SELECT
FK = fk.name,
FKTable = QUOTENAME(OBJECT_SCHEMA_NAME(fkcol.[object_id]))
+ '.' + QUOTENAME(OBJECT_NAME(fkcol.[object_id])),
FKCol = fkcol.name,
' references => ',
PKTable = QUOTENAME(OBJECT_SCHEMA_NAME(pkcol.[object_id]))
+ '.' + QUOTENAME(OBJECT_NAME(pkcol.[object_id])),
PKCol = pkcol.name
FROM sys.foreign_keys AS fk
INNER JOIN sys.foreign_key_columns AS fkc
ON fk.[object_id] = fkc.constraint_object_id
INNER JOIN sys.columns AS fkcol
ON fkc.parent_object_id = fkcol.[object_id]
AND fkc.parent_column_id = fkcol.column_id
INNER JOIN sys.columns AS pkcol
ON fkc.referenced_object_id = pkcol.[object_id]
AND fkc.referenced_column_id = pkcol.column_id
ORDER BY fkc.constraint_column_id;
Source:
The case against INFORMATION_SCHEMA views

Related

Inner join statement in bind variables

I use bind variables to get data from the SQL tables.
The code:
string commandText = string.Format("SELECT {0} FROM {1} WHERE {2} ORDER BY {3}", fields, from, whereStr, orderBy);
DbCommand command = GetSqlCommandWrapper(commandText);
int index = 0;
foreach (var item in whereParams)
{
command.Parameters.Add(new OracleParameter(item, whereParamsBy[index++]));
}
DataTable db = new DataTable();
DataSet dataSet = null;
dataSet = CurrentDatabase.ExecuteDataSet(command);
The CommandText:
fields = "TableA.*";
from = "TableA INNER JOIN TableB ON TableA .id = TableB.id";
whereStr = "TableA .id > 10";
orderBy = "TableA .id, TableB .id";
Everything works fine until I have to get data from 2 tables like the query:
select * from a inner join b on a.id = b.id where....
Someone can tell me how to do it?
Thanks in advance
The overall approach is not very nice, but in terms of getting what you want, this should do it.
fields = "*";
from = "TableA AS a INNER JOIN TableB AS B ON a.id = b.id";
whereStr = "a.id > 10";
orderBy = "a.id, b.id";
Set your input parameters to that, or just paste it in before the code you gave us and it will produce the command SELECT * FROM TableA AS a INNER JOIN TableB AS B ON a.id = b.id WHERE a.id > 10 ORDER BY a.id, b.id
Please note that orderBy = "a.id; DROP TABLE TableA;"; is a scary prospect

sql command yields empty results that aren't empty in sql server

So, I'm trying to build a code generator that will extract indexes from a database and make a class that will filter based on an index.
Below code works in SQL-server and yields 2 records. But my SqlDataReader yields zero records.
Provided example for 1 table with index.
Hoping someone can help me out here.
Code in SQL server:
create table Agent(
ID bigint constraint PK_Agent primary key identity(1,1),
LastName nvarchar(50) not null,
FirstName nvarchar(50) not null,
index IN_Agent_Name nonclustered (LastName, FirstName)
)
select t.object_id,
s.name as schemaname,
t.name as tablename,
i.index_id,
i.name as indexname,
index_column_id,
c.name as columnname
from sys.tables t
inner join sys.schemas s on t.schema_id = s.schema_id
inner join sys.indexes i on i.object_id = t.object_id
inner join sys.index_columns ic on ic.object_id = t.object_id and ic.index_id = i.index_id
inner join sys.columns c on c.object_id = t.object_id and ic.column_id = c.column_id
where i.index_id > 0
and i.type in (1, 2)
and i.is_primary_key = 0
and i.is_unique_constraint = 0
and i.is_disabled = 0
and i.is_hypothetical = 0
and ic.key_ordinal > 0
and t.name like 'Agent'
and i.name like 'IN_Agent_Name'
Code in VS:
public static TableIndex GetIndex(string indexName, string tableName)
{
TableIndex index = null;
using (var conn = new SqlConnection("Server=localhost;Database=VenturaERD;User Id=VenturaDBUser;Password = Ventura;"))
{
conn.Open();
var cmd = new SqlCommand("select t.object_id, s.name as schemaname, t.name as tablename, i.index_id, i.name as indexname, index_column_id, c.name as columnname from sys.tables t inner join sys.schemas s on t.schema_id = s.schema_id inner join sys.indexes i on i.object_id = t.object_id inner join sys.index_columns ic on ic.object_id = t.object_id and ic.index_id = i.index_id inner join sys.columns c on c.object_id = t.object_id and ic.column_id = c.column_id where i.index_id > 0 and i.type in (1, 2) and i.is_primary_key = 0 and i.is_unique_constraint = 0 and i.is_disabled = 0 and i.is_hypothetical = 0 and ic.key_ordinal > 0 and t.name like '" + tableName + "' and i.name like '" + indexName + "'")
{
Connection = conn
};
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
index = new TableIndex()
{
TableId = reader.GetInt32(reader.GetOrdinal("object_id")),
TableName = reader.GetString(reader.GetOrdinal("tablename")),
IndexId = reader.GetInt32(reader.GetOrdinal("index_id")),
IndexName = reader.GetString(reader.GetOrdinal("indexname")),
Columns = new List()
{
new IndexColumn()
{
ColumnName = reader.GetString(reader.GetOrdinal("columnname")),
Order=reader.GetInt32(reader.GetOrdinal("index_column_id"))
}
}
};
while (reader.Read())
{
index.Columns.Add(new IndexColumn()
{
ColumnName = reader.GetString(reader.GetOrdinal("columnname")),
Order = reader.GetInt32(reader.GetOrdinal("index_column_id"))
});
}
}
reader.Close();
}
}
return index;
}
Please check the users rights, I believe with only the public rights the user will not get any data.
Make sure the user you are connecting to with SQL management studio and the user in the connection string are the same.
The user (in my tests at least) needs at least the db_datareader role.

Syntax for inner Join query for 4 table

I want to get the data from my access database and show the data at Datagridview. my tables are:
TABLE 1(AB) TABLE 2(CD) TABLE 3(EF) TABLE 4(GH)
------------- ----------------- ------------- -------------------
SID SName CID TID TID TName Tprice FID CID FCp FPID FID FCp Fprice
The query which I am using to retrieve the data in C# is:
OleDbCommand command1 = new OleDbCommand();
command1.Connection = connection;
command1.CommandText = "select T.TName, T.Tprice, P.FCp, P.Fprice from (([AB] S inner join [CD] T on S.TID = T.TID) (inner join [EF] C on S.CID = C.CID) inner join [GH] P on C.FID = P.FID where (S.SID = 2) ";
OleDbDataReader myreader = command1.ExecuteReader();
while (myreader.Read())
{
//DATA IS READ HERE
}
The error which I am getting is:
Syntax error at JOIN expression
I want TName, Tprice, FCp(TABLE 4), Fprice as my output. Am I doing it right or is there any other way to do that.
In your from you have opening parentheses with no closing (the first one).
Besides fixing the parentheses it is much more readable if you jump lines:
command1.CommandText = #"select T.TName, T.Tprice, P.FCp, P.Fprice
from (([AB] S
inner join [CD] T on S.TID = T.TID)
inner join [EF] C on S.CID = C.CID)
inner join [GH] P on C.FID = P.FID
where S.SID = 2";
(Access requires parentheseses)

Get full name of SQL Server tables

Just some extract from the code:
cmd.CommandText = "SELECT TABLE_TYPE, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
using (dataReader = cmd.ExecuteReader())
{
string tableName = (string)dataReader["TABLE_NAME"];
}
Should I use "table_schema" column of th INFORMATION_SCHEMA.TABLES like this:
dbo.string fullname = dataReader["table_schema"]+"."+dataReader["table_name"];
Or should I use another method? I need to insert this fullname in a script for insertion table data.
Try something like this:
SELECT
SchemaName = s.Name,
TableName = t.Name,
FullName = s.Name + '.' + t.Name
FROM
sys.tables t
INNER JOIN
sys.schemas s ON s.schema_id = t.schema_id
This should give you the schema, the table name, and the combined (schema).(table name) notation

C# SQL Query Producing Different Results than Management Studio

SELECT
wl.WatchListId, wl.Code, wl.[Description], wl.DateCreated,
wl.CreatedBy, wl.DateModified, wl.ModifiedBy,
wpi.ParameterExpression as IndividualExpression,
wpb.ParameterExpression as BusinessExpression,
wpd.ParameterExpression as DefaultExpression,
CASE
WHEN EXISTS(SELECT 1 FROM SourceWatchList
WHERE SourceId = #SourceId AND WatchListId = wl.WatchListId)
THEN 1 ELSE 0
END AS IsActive
FROM
[WatchList] wl
LEFT JOIN
SourceWatchList swl ON wl.WatchListId = swl.WatchListId AND swl.SourceId = #SourceId
LEFT JOIN
(SELECT
ParameterExpression, SourceId, WatchListId
FROM WatchListParameter
WHERE EntityType = 'INDIVIDUAL') wpi ON wpi.SourceId = #SourceId
AND wpi.WatchListId = wl.WatchListId
LEFT JOIN
(SELECT
ParameterExpression, SourceId, WatchListId
FROM WatchListParameter
WHERE EntityType = 'BUSINESS') wpb ON wpb.SourceId = #SourceId
AND wpb.WatchListId = wl.WatchListId
LEFT JOIN
(SELECT ParameterExpression, SourceId, WatchListId
FROM WatchListParameter
WHERE EntityType = 'DEFAULT') wpd ON wpd.SourceId = #SourceId
AND wpd.WatchListId = wl.WatchListId
WHERE
wl.IsActive = 1
I have the above query. Pretty simple.
Here's a snippet from SQL Server Management Studio table :
and from the debugger in Visual Studio :
The Visual Studio table has no data in the 3 Expression Columns while the Management Studio (correctly) does. Can any tell me why that is, and what steps I can take to resolve the issue?
I'm 100% sure I've used the same parameter between the two as well.
string sql = #"SELECT wl.WatchListId,wl.Code,wl.[Description],wl.DateCreated,
wl.CreatedBy,wl.DateModified,wl.ModifiedBy,
wpi.ParameterExpression as IndividualExpression,
wpb.ParameterExpression as BusinessExpression,
wpd.ParameterExpression as DefaultExpression,
CASE WHEN EXISTS(Select 1 FROM SourceWatchList
WHERE SourceId = #SourceId AND WatchListId = wl.WatchListId)
THEN 1 ELSE 0 END AS IsActive
FROM [WatchList] wl
LEFT JOIN SourceWatchList swl on wl.WatchListId = swl.WatchListId and swl.SourceId = #SourceId
LEFT JOIN (Select ParameterExpression, SourceId, WatchListId FROM WatchListParameter WHERE EntityType = 'INDIVIDUAL') wpi
ON wpi.SourceId = #SourceId AND wpi.WatchListId = wl.WatchListId
LEFT JOIN (Select ParameterExpression, SourceId, WatchListId FROM WatchListParameter WHERE EntityType = 'BUSINESS') wpb
ON wpb.SourceId = #SourceId AND wpb.WatchListId = wl.WatchListId
LEFT JOIN (Select ParameterExpression, SourceId, WatchListId FROM WatchListParameter WHERE EntityType = 'DEFAULT') wpd
ON wpd.SourceId = #SourceId AND wpd.WatchListId = wl.WatchListId
where wl.IsActive = 1";
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["WatchListCompliance"].ConnectionString);
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#SourceId", SourceId);
DataTable dt = new DataTable();
using (SqlDataAdapter a = new SqlDataAdapter(cmd))
{
a.Fill(dt);
}
This might be a problem with types and equality. You can try using SqlDataType when you define the parameters on the sqlcommand. When you use addwithvalue without declaring a type it could be selecting the wrong type. Then you should use the type you declared in the sql expression.
cmd.Parameters.Add("#SourceId", SqlDbType.VarChar, 20);
cmd.Parameters["#SourceId"].Value = SourceId;
Wild guess: are you sure your session is configured the same way? Especially the SET ANSI_NULLS option, which changes how comparison behaves with regard to null values.
It is worth noting that some databases may have unusual default values, but some clients (e.g. ADO.NET) explicitely define their own values for the session when they connect, even if you don't ask them to.

Categories

Resources