Getting duplication with inner join and left join - sql

I am trying to fetch following information for the table of the columns :
1) Primary key.
2) IsNullable
3) Is_Identity.
Query :
SELECT c.name 'Column Name',c.is_nullable,c.is_identity,ISNULL(i.is_primary_key, 0) 'Primary Key' FROM sys.columns c INNER JOIN sys.types t ON c.user_type_id = t.user_type_id LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id WHERE c.object_id = OBJECT_ID('[HumanResources].[Employee]')
I am working with Adventure Works database but the problem here is I am getting OrganizationNode column 2 times and I am unable to understand the reason.
For other tables I am not getting this issue but I am only getting this problem for the table [HumanResources].[Employee].
Below is the link to download database bak file :
https://www.dropbox.com/s/nutfat17b73boav/AdvantureWorksSeScript.bak?dl=0
I have also tried distinct but because of that it shuffled up the ordering of the column as you can see in the second output. BirthDate is coming at the top instead of BusinessEntityID.
I would not like to use distinct as my below query is working fine with all other tables but I am not getting why it is giving duplicate columns in case of [HumanResources].[Employee] table or may be I have tested it with few tables and query may not work properly for few other databases.
I am not sure whether my query is correct or not to work in all the scenarios to fetch above 3 things(pk,inullable etc.) which I mention.

Try this
SELECT name,
is_identity,
is_nullable,
is_primary_key
FROM (
SELECT c.name,
c.column_id,
c.is_identity,
c.is_nullable,
ISNULL(i.is_primary_key,0) is_primary_key,
ROW_NUMBER() OVER(PARTITION BY c.name ORDER BY c.name) rn
FROM sys.columns c
JOIN sys.tables t
ON t.object_id = c.object_id
LEFT JOIN sys.index_columns ic
ON c.object_id = ic.object_id AND c.column_id = ic.column_id
LEFT JOIN sys.indexes i
ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE t.name = 'Employee'
)keys
WHERE rn = 1
ORDER BY column_id

Related

Optimization for system views

I have a query for getting columns data from the Microsoft SQL Server database. I need to speed up my query because I will run it multiple times each minute. Currently, my query took about 60ms to execute on SQL Server. It would be awesome too cut it down to 30 or 40ms.
Execution plan: plan
Is there any possible way for optimization?
SELECT
c.name as 'columnName',
t.name as 'dataType',
t.name + '(' + cast(c.max_length as varchar(50)) +')' AS 'dataTypeFull',
ISNULL(sep.value,'') AS 'columnDescription',
CASE
WHEN fk.object_id is not null THEN fk.name ELSE NULL
END AS 'foreignKey',
pk_tab.*,
ISNULL(i.is_primary_key,0) AS 'primaryKey'
FROM
sys.tables tab
INNER JOIN
sys.columns c ON c.object_id = tab.object_id
LEFT OUTER JOIN
sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN
sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
LEFT OUTER JOIN
sys.foreign_key_columns fk_cols ON fk_cols.parent_object_id = tab.object_id
AND fk_cols.parent_column_id = c.column_id
LEFT OUTER JOIN
sys.foreign_keys fk ON fk.object_id = fk_cols.constraint_object_id
LEFT OUTER JOIN
sys.tables pk_tab ON pk_tab.object_id = fk_cols.referenced_object_id
LEFT JOIN
sys.extended_properties sep ON c.object_id = sep.major_id
AND c.column_id = sep.minor_id
AND sep.name = 'MS_Description'
JOIN
sys.types t ON c.user_type_id = t.user_type_id
WHERE
c.object_id = Object_id('some-table-name')

Use CTE To Create Subset of Data

I'm wanting to create a subset of data for testing purposes, where I start with a Users table, select X of type a, Y of type b, Z of type c (and so on), and create a new table containing those users. I then need to to create all related tables (with the necessary records), and then all related tables to those...
I'm fairly certain the best way to do this is a recursive common table expression, however I don't have much experience with those and was hoping someone could help me out. So far I have the below, but have noticed two things:
Level 2 seems to contain everything that was in level 1, in addition to the new records I'd expect
This doesn't actually create/insert the records yet (I still need help with that)
WITH cte AS
(
SELECT DISTINCT fk.object_id, fk.schema_id, fk.parent_object_id, fc.parent_column_id, t.schema_id AS referenced_schema_id, fk.referenced_object_id, ic.column_id AS referenced_column_id, 1 AS Level
FROM sys.foreign_keys fk
INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
INNER JOIN sys.foreign_key_columns fc ON fk.object_id = fc.constraint_object_id
INNER JOIN sys.indexes i ON fk.referenced_object_id = i.object_id AND i.is_primary_key = 1
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE fk.type = 'F'
AND fk.referenced_object_id = OBJECT_ID(N'dbo.Users', N'U')
UNION ALL
SELECT fk.object_id, fk.schema_id, fk.parent_object_id, fc.parent_column_id, t.schema_id, fk.referenced_object_id, ic.column_id AS referenced_column_id, cte.Level + 1
FROM sys.foreign_keys fk
INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
INNER JOIN sys.foreign_key_columns fc ON fk.object_id = fc.constraint_object_id
INNER JOIN sys.indexes i ON fk.referenced_object_id = i.object_id AND i.is_primary_key = 1
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
INNER JOIN cte ON fk.referenced_object_id = cte.parent_object_id
WHERE fk.type = 'F'
AND fk.parent_object_id <> cte.referenced_object_id
),
cteHierarchy AS (
SELECT DISTINCT
OBJECT_NAME(cte.object_id) AS ReferringKey,
SCHEMA_NAME(cte.schema_id) AS ReferringSchema,
OBJECT_NAME(cte.parent_object_id) as ReferringTable,
COL_NAME(cte.parent_object_id,cte.parent_column_id) ReferringColumn,
SCHEMA_NAME(cte.referenced_schema_id) AS ReferencedSchema,
OBJECT_NAME(cte.referenced_object_id) as ReferencedTable,
COL_NAME(cte.referenced_object_id,cte.referenced_column_id) ReferencedColumn,
Level
FROM cte
)
SELECT *
FROM cteHierarchy
ORDER BY Level, ReferencedSchema, ReferencedTable, ReferringTable;
Thank you for your assistance!

basic sql syntax how do I reference my database?

I was able to get the below code to work for what I needed but only when I include the prefix "use" at the top. When I try to include the name of the database in the "where" clause, I was unsuccessful (would get back column headers and no rows).
What syntax do I need to use to reference the database object without using "use"?
(Note: This question is for my own curiosity. the "use" prefix effectively solves my problem for running the metadata query.)
use [Investment Data 1]
SELECT
c.name 'Column Name',
t.Name 'Data type',
c.max_length 'Max Length',
c.precision ,
c.scale ,
c.is_nullable,
ISNULL(i.is_primary_key, 0) 'Primary Key'
FROM
sys.columns c
INNER JOIN
sys.types t ON c.user_type_id = t.user_type_id
LEFT OUTER JOIN
sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN
sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE
c.object_id = OBJECT_ID('dbo.hurricane_landfall_2017')
You can add the databasename to each object, there's a four part naming syntax in SQL Server: server.database.schema.object
SELECT
c.name 'Column Name',
t.Name 'Data type',
c.max_length 'Max Length',
c.precision ,
c.scale ,
c.is_nullable,
ISNULL(i.is_primary_key, 0) 'Primary Key'
FROM
[Investment Data 1].sys.columns c
INNER JOIN
[Investment Data 1].sys.types t ON c.user_type_id = t.user_type_id
LEFT OUTER JOIN
[Investment Data 1].sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN
[Investment Data 1].sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE
c.object_id = OBJECT_ID('dbo.hurricane_landfall_2017')
In each query that you use, you have to set the database that you want the sql-server come to.
We have 2 ways for that:
1. "Use" command (like what you wrote)
2. Select for the listbox (left side, at the top part) your database
Note that the "use" command it's more stronger from the listbox way.
I hope I helped.
Goodluck!

Get list of all columns with complete details (Identity, nullabel,primary key) in sql server without duplicate column

I have need to automate the process of database upgrade process.
So by getting the column structure of a table i need to create update/insert/create sql statement's .
BY problem was i am getting duplicate column name as constrains are also coming in the list which i don't need. So is there a way to restrict constraints in the result set.
I am using below query
SELECT c.NAME 'Column Name'
,t.NAME 'Data type'
,c.max_length 'Max Length'
,c.precision
,c.scale
,c.is_nullable AS 'Is Nullable'
,c.is_identity AS 'Is Identity'
,ISNULL(i.is_primary_key, 0) 'Primary Key'
,i.type_desc
FROM fblfeb12.sys.columns c
INNER JOIN fblfeb12.sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN fblfeb12.sys.index_columns ic ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
LEFT OUTER JOIN fblfeb12.sys.indexes i ON ic.object_id = i.object_id
AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID('table name')
Result set:
Pms_ID uniqueidentifier 16 0 0 0 0 CLUSTERED
Pms_PRODMODELID uniqueidentifie 16 0 0 1 0NONCLUSTERED
Pms_PRODMODELID uniqueidentifier 16 0 0 10NONCLUSTERED
Pms_PRODMODELID uniqueidentifier 16 0 0 10NONCLUSTERED
Pms_ATTRIBUTEID uniqueidentifier 16 0 0 10NONCLUSTERED
Pms_ATTRIBUTEID uniqueidentifier 16 0 0 0NONCLUSTERED
Pms_ATTRIBUTEID uniqueidentifier 16 0 0 1NONCLUSTERED
where PRODMODELID , ATTRIBUTEID comes 3 times.
I need only foreign key column but here I am getting index,Constraints which i don't need.
I need column name, data-type,identity,primary key, null able, foreign key.
Can you provide me any better solution,If i am doing anything wrong?
The problem is that you are including all indexes on the column. Consider this simple table:
CREATE TABLE #T (ID INT NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY);
CREATE INDEX IX_T_ID ON #T (ID);
When you run an adaption of you query:
SELECT c.name, i.name, i.is_primary_key
FROM tempdb.sys.columns c
LEFT OUTER JOIN tempdb.sys.index_columns ic
ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
LEFT OUTER JOIN tempdb.sys.indexes i
ON ic.object_id = i.object_id
AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID('tempdb..#T');
The column name will be duplicated for each index that includes that column (either key or non key), so the result will be:
name name is_primary_key
ID PK_T_ID 1
ID IX_T_ID 0
Since you only care about the primary key, you can apply the a filter in the join to sys.indexes to only return the primary keys, to do this effectively though you need to make the join between index_columns and indexes an INNER JOIN, but maintain the OUTER JOIN from columns to index_columns which involves slightly rearranging the joins, so the above would become:
SELECT c.name, i.name, i.is_primary_key
FROM tempdb.sys.columns c
LEFT OUTER JOIN (tempdb.sys.index_columns ic
INNER JOIN tempdb.sys.indexes i
ON ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND i.is_primary_key = 1) -- ONLY PRIMARY KEYS
ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
WHERE c.object_id = OBJECT_ID('tempdb..#T');
This removes the duplicate result. Finally you can query sys.foreign_key_columns to find out if the column references another table giving a final query of:
SELECT c.NAME AS [Column Name]
,t.NAME AS [Data type]
,c.max_length AS [Max Length]
,c.precision
,c.scale
,c.is_nullable AS [Is Nullable]
,c.is_identity AS [Is Identity]
,ISNULL(i.is_primary_key, 0) [Primary Key]
,i.type_desc
,OBJECT_SCHEMA_NAME(fk.object_id) + '.' + OBJECT_NAME(fk.object_id) + ' (' + fk.Name + ')' AS [Foreign Key]
FROM sys.columns c
INNER JOIN sys.types t
ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN (sys.index_columns ic
INNER JOIN sys.indexes i
ON ic.object_id = i.object_id
AND i.is_primary_key = 1
AND ic.index_id = i.index_id)
ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
LEFT JOIN sys.foreign_key_columns fkc
ON fkc.parent_object_id = c.object_id
AND fkc.parent_column_id = c.column_id
LEFT JOIN sys.columns fk
ON fk.object_id = fkc.referenced_object_id
AND fk.column_id = fkc.referenced_column_id
WHERE c.object_id = OBJECT_ID('table')
ORDER BY c.Column_ID;
N.B I have changed your column aliases from single quotes to brackets as using single quotes is deprecated (not to mention easily confused with string literals)
What happens if you use the "DISTINCT"? so
SELECT distinct c.NAME...
"I need only foreign key column but here I am getting index,Constraints which i don't need."
If you need to get foreign key columns could you not use this SO post . And remove the joins to sys.index_column and sys.indexes?

Identifying all columns included in composite PK throughout the database

I want to identify all the columns which are part of PK (composite or non-composite) throughout all the tables in my database. How can I do that with a sql statement? Desired result set:
TABLE NAME | COLUMN NAME | DATA TYPE | CONSTRAINT |
in Sql Server exists a system view to show all constraints.
Try this:
select * from sys.key_constraints where type_desc = 'PRIMARY_KEY_CONSTRAINT'
Something like this:
select
t.name as [TABLE NAME],
c.name as [COLUMN NAME],
ty.name as [DATA TYPE],
i.name as [CONSTRAINT]
from sys.index_columns ic
join sys.indexes i
on ic.object_id = i.object_id
and ic.index_id = i.index_id
join sys.columns c
on ic.object_id = c.object_id
and ic.column_id = c.column_id
join sys.tables t
on ic.object_id = t.object_id
join sys.types ty
on c.user_type_id = ty.user_type_id
where i.is_primary_key = 1
I copied this into my snippets folder long ago, so I can't take credit for it, but this works well...
select s.name as TABLE_SCHEMA, t.name as TABLE_NAME, k.name as CONSTRAINT_NAME, c.name as COLUMN_NAME, ic.key_ordinal AS ORDINAL_POSITION
from sys.key_constraints as k
join sys.tables as t
on t.object_id = k.parent_object_id
join sys.schemas as s
on s.schema_id = t.schema_id
join sys.index_columns as ic
on ic.object_id = t.object_id
and ic.index_id = k.unique_index_id
join sys.columns as c
on c.object_id = t.object_id
and c.column_id = ic.column_id
where k.type = 'PK';
SELECT o.name AS TableName, c.Name AS ColumnName, t.name AS DataType, kc.name AS ContraintName
FROM sys.key_constraints kc
INNER JOIN sys.index_columns ic ON kc.parent_object_id = ic.object_id AND kc.unique_index_id = ic.index_id
INNER JOIN sys.columns c ON ic.column_id = c.column_id AND c.object_id = ic.object_id
INNER JOIN sys.objects o ON ic.object_id = o.object_id
INNER JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE kc.type = 'PK'
ORDER BY TableName,ic.key_ordinal