How to select all non unique columns from my DB? - sql-server

I'm learning SQL utilizing the common Microsoft AdventureWorks2014 sample database in SQL Server 2014.
I just learned about HAVING and Information Schema today and am trying to combine the two.
Reason being, I'd really like to quickly tell which columns from all tables are shared. This works:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
ORDER BY COLUMN_NAME, TABLE_SCHEMA
But... the output gives me unique column names that only slows me down.
I've tried applying answers from "How to select non 'unique' rows" (among 5-7 other SOF pages!) such as:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
GROUP BY COLUMN_NAME
HAVING COUNT(COLUMN_NAME) > 1
...but I get this error:
Msg 8120, Level 16, State 1, Line 1 Column
'information_schema.columns.TABLE_SCHEMA' is invalid in the select
list because it is not contained in either an aggregate function or
the GROUP BY clause.

You can use your query, to retrive all the columns that are shared, and then join to the original table for all the information (schema, name):
SELECT t.TABLE_SCHEMA,
t.table_name,
t.column_name
FROM INFORMATION_SCHEMA.COLUMNS t
INNER JOIN (
SELECT s.column_name
FROM INFORMATION_SCHEMA.COLUMNS s
GROUP BY s.column_name
HAVING COUNT(s.column_name) > 1
) tt ON (t.column_name = tt.column_name)

SELECT *
FROM (
SELECT
col = c.name,
obj_name = o.name,
sch_name = SCHEMA_NAME(o.[schema_id]),
col_type = TYPE_NAME(c.system_type_id),
RowNum = COUNT(1) OVER (PARTITION BY c.name, o.[type] ORDER BY 1/0)
FROM sys.columns c
JOIN sys.objects o ON c.[object_id] = o.[object_id]
WHERE o.[type] = 'U'
) t
WHERE t.RowNum > 1
ORDER BY t.col
Output:
col obj_name sch_name col_type
----------------------- ------------------- --------- ------------
dbid spt_fallback_usg dbo smallint
dbid spt_fallback_db dbo smallint
xserver_name spt_fallback_usg dbo varchar
xserver_name spt_fallback_db dbo varchar
xserver_name spt_fallback_dev dbo varchar

Related

Run query for each database in a list and append results

I have the below code to pull the row and column counts from each table within a database (e.g., db1). But I have several databases (e.g., db1, db2 etc.) , so manually updating the database name in the USE statement for every run isn't very convenient. Is there a way to pass a list of database names in a cursor (or something else that allows iteration) and then run the below query for every database in the list, appending the results from each run? I can get the list of database names from this query select name from master.dbo.sysdatabases where name like '%db%'.
USE [db1]
;with [rowCount] as
(
SELECT DB_NAME() as [DB_Name],
QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) AS [TableName],
SUM(sPTN.Rows) AS [RowCount]
FROM SYS.OBJECTS AS sOBJ
INNER JOIN SYS.PARTITIONS AS sPTN
ON sOBJ.object_id = sPTN.object_id
WHERE
sOBJ.type = 'U'
AND sOBJ.is_ms_shipped = 0x0
AND index_id < 2 -- 0:Heap, 1:Clustered
GROUP BY
sOBJ.schema_id
,sOBJ.name
)
,columnCount as
(
select
QUOTENAME(col.TABLE_SCHEMA) + '.' + QUOTENAME(col.TABLE_NAME) AS [TableName],
count(*) as ColumnCount
from INFORMATION_SCHEMA.COLUMNS col
inner join INFORMATION_SCHEMA.TABLES tbl
on col.TABLE_SCHEMA = tbl.TABLE_SCHEMA
and col.TABLE_NAME = tbl.TABLE_NAME
and tbl.TABLE_TYPE <> 'view'
group by
QUOTENAME(col.TABLE_SCHEMA) + '.' + QUOTENAME(col.TABLE_NAME)
)
select r.[DB_Name], r.TableName, r.[RowCount], c.ColumnCount
from [rowCount] r
inner join columnCount c
on r.TableName = c.TableName
ORDER BY r.[TableName]

Count columns of a result from with block?

I am exploring provided code. I have various CTEs, and I want to count the columns in each CTE? Or select the differences in columns per CTE?
The below code will grab the count from a table in the DB, but I'm not sure how to apply this to CTEs?
select count(*)
from INFORMATION_SCHEMA.columns
where TABLE_CATALOG = 'db_name'
and TABLE_NAME = 'table_name'
First Create CTE and then insert just one record into temp table , then query the sys.columns table to get the list of the columns in CTE
IF OBJECT_ID('tempdb.dbo.#temp', 'U') IS NOT NULL
DROP TABLE #temp;
go
With CTE1 as (select * from table_name)
select top 1 * into #temp from CTE1
SELECT count(*) FROM tempdb.sys.columns where object_id = object_id('tempdb..#temp')

Simple query gone wrong

I must be having an off day. This should be obvious but I don't get it.
-- check for necessary updates to dbnotes
select count(distinct table_name)
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS]
returns 46
select count(distinct table_name)
from dbnotes
returns 44
select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS]
where table_name not in (select distinct table_name from dbnotes)
order by table_name
returns nothing
select distinct table_name
from dbnotes
where table_name not in (select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS])
order by table_name
returns nothing
What am I missing guys?
You are using not in. If any value from the subquery is NULL, nothing will be returned.
With a subquery, always use not exists. It has the right semantics:
select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS] c
where not exists (select 1
from dbnotes d
where d.table_name = c.table_name
);
I am pretty sure that tables have to have at least one column, so you might as well use information_schema.tables instead. It saves you the distinct:
select table_name
from ccsv4.information_schema.tables t
where not exists (select 1
from dbnotes d
where d.table_name = t.table_name
);

What is the T-SQL syntax to get the length of each column of a specific Table?

My database is hosted on Microsoft SQL Server 2012 and I need to write a T-SQL query to pull the length of each column of a specific table.
Assuming my database is called mydatabase and the table is called table1 with 3 columns (namely col1, col2 and col3), how do I write my sql query to get that information?
Ideally, I want the output to be something like this:
ColumnName Length
col1 50
col2 30
col3 25
Additional info: I will need to run this query on several other tables where I don't know the number or names of the columns therein. So the query should output the names of the columns with their respective column length.
I assume by length you mean, for example, that if it is a varchar(50) it has a length of 50. If it's a decimal(18,2) then you want to know Scale 18, Precision 2. This should help:
SELECT c.[name] AS ColumnName, st.[name] AS DataType,
CASE WHEN st.[name] IN ('varchar','nvarchar') THEN c.max_length END AS Max_Length,
CASE WHEN st.[name] NOT IN ('varchar','nvarchar') THEN c.scale END AS Scale,
CASE WHEN st.[name] NOT IN ('varchar','nvarchar') THEN c.[precision] END AS [Precision]
FROM sys.tables t
JOIN sys.columns c ON t.object_id = c.object_id
JOIN sys.systypes st ON c.system_type_id = st.xtype
WHERE t.[name] = 'YourTableName';
If you are looking for a Query that will return the Maximum permitted length of a column, Then you can View it from the INFORMATION_SCHEMA.COLUMN view
SELECT
ORDINAL_POSITION,
COLLATION_NAME,
CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'YourTableName'
Or if you are looking for the Maximum Lenght of the Data Stored is Each Column
Use MAX() and LEN() function
SELECT MAX(LEN(Col1)) FROM YourTable
You can use COL_LENGTH to get this (more information can be found here)
You could write something like the following:
Select COL_LENGTH ( 'table1' , 'Col1' )
Select COL_LENGTH ( 'table1' , 'Col2' )
Select COL_LENGTH ( 'table1' , 'Col3' )
EDIT:
With the extra information provided I think the below is what you are looking for:
SELECT
t.name AS 'Table_Name'
,c.name AS 'Column_Name'
,I.CHARACTER_MAXIMUM_LENGTH
,I.DATA_TYPE
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN INFORMATION_SCHEMA.COLUMNS I on I.COLUMN_NAME = c.name
You will probably have to add a where clause in as this is currently looking for everything on a database.
By joining the sys.columns and sys.tables with information_schema.columns
You can find the length of columns/tables with out needing to know the name.

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

Resources