Ordinal column position from sys schema - sql-server

How can I get the following query, working on both views and tables, by using the SYS schema?
SELECT c.column_name,c.ordinal_position
from information_schema.columns c
where TABLE_SCHEMA='dbo'
and table_name='def_transaction_pt'
I have used the following until now, but I don't know where to get the ordinal column position from:
select *
from
sys.objects o
inner join sys.columns c on o.object_id=c.object_id
where o.name='def_transaction_pt'

You are on the right path...
DECLARE #ObjectName sysname = 'def_transaction_pt',
#SchemaName sysname = 'dbo'
SELECT c.name As Column_Name, ROW_NUMBER() OVER(ORDER BY c.column_id) As Ordinal_Position
FROM sys.columns AS c
JOIN sys.objects AS o
ON c.object_id = o.object_id
JOIN sys.schemas AS s
ON o.schema_id = s.schema_id
WHERE o.name = #ObjectName
AND s.name = #SchemaName
Note: I've only joined the sys.schemas to enable filtering using schema name,
and sys.objects to allow filtering using table/view name
After reading the comments and documentation, I've decided to adopt Larnu's suggestion about using row_number instead of the column_id directly.
Also, I've changed All_columns to Columns since it contains the columns from both tables and views.
BTW, the official documentation of information_schema.Columns describes the Ordinal_Position as "Column identification number." - which might be just the same as the Column_Id - It would require more testing to figure out that part.
BTW #2: Though you can't change the column's ordinal position using an alter table, it is possible to do using the visual designer (which in turn, drops and re-creates the table) - and if you do that, the column id for all columns is re-calculated, so it's always corresponding with the actual ordinal position (gaps aside).

Related

Filter table in long format i.e. keep rows if condition in group is satisfied

I want to query and see which table do not have a column called hello.
I first use a query from this question: Find all tables containing column with specified name - MS SQL Server
SELECT c.name AS 'ColumnName'
,t.name AS 'TableName'
,c.object_id
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name in (select colname from someTable) -- table with one column containing table names.
So this gives me a table in long format i.e. every table has many rows. Now I want figure out which table does not have a column called hello.
Here an example for the query with a cte. However, you have to consider that the same table name might exist in different schemas, so if you work with schemas you should add a join to sys.schemas and include this in your query:
WITH cteColTabs AS(
SELECT t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name in (select colname from someTable) -- table with one column containing table names.
AND c.name = N'hello'
)
SELECT t.Name AS TableName
FROM sys.tables t
LEFT JOIN cteColTabs ct ON ct.TabName = t.Name
WHERE ct.TableName IS NULL

How to tell whether a column is of sysname type

It's a common question how to find the columns in a table by querying the system columns. For example SQL server query to get the list of columns in a table along with Data types, NOT NULL, and PRIMARY KEY constraints gives a query which works in most cases. However, a column of type sysname is returned as plain nvarchar. In Management Studio (SSMS) scripting out a table definition will correctly give the sysname type. And so will sp_columns. But how can I find whether a column is of sysname type using an SQL query against the system tables? (I don't want to run sp_columns separately for each table.)
In case you are wondering what the sysname type is to start with, What is SYSNAME data type in SQL Server? gives some info.
To give more detail:
create view some_table_names as select name from sys.tables
Then running
sp_columns 'some_table_names'
reports type_name=sysname. But a simple query against sys.columns gives just varchar:
select type_name(c.system_type_id)
from sys.objects t
join sys.columns c
on t.object_id = c.object_id
where t.name = 'some_table_names'
I had a look at the definition of sp_columns to see if I could do the same thing. It looks up the column details in a system table sys.spt_columns_odbc_view. But this is apparently some top secret internal table that can only be queried from a direct administrator connection (DAC) or from SSMS. (See What is spt_columns_odbc_view and why is it not accessible?) The sp_columns proc manages to query this view even though I am not running it from Management Studio or over a DAC. But I don't know how to repeat that trick in my own code.
Is there some other way to tell whether a column is of sysname type?
The sys.types catalog view exposes data types that can be specified in DDL. You can join to this view on user_type_id to identify column type. As you can see from this query, sysname is not an internal secret type.
SELECT c.Name AS ColumnName, ty.name AS TypeName, c.max_length AS ColumnLengthBytes
FROM sys.objects t
JOIN sys.columns c ON t.object_id = c.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
WHERE t.name = N'test_table';
sysname is similar to a user-defined type. It differs from a UDT created with CREATE TYPE in that the is_user_defined column of sys.types will be zero instead of one since it's defined by SQL Server rather than a user.
One can also join on system_type_id to also return both the user and base system type.
SELECT c.Name AS ColumnName, ty.name AS TypeName, c.max_length AS ColumnLengthBytes
FROM sys.objects t
JOIN sys.columns c ON t.object_id = c.object_id
JOIN sys.types ty ON ty.system_type_id = c.system_type_id
WHERE t.name = N'test_table';
First of all, there's no valid case for storing data as sysname, even for maintenance scripts. For a script you want the actual name of a table or column, so storing it in an nvarchar(128) field is perfectly fine.
The system itself treats sysname as a user type in the sys.types table. If you check the record, you'll see that the database itself tells you this is a nvarchar. If you want to return that alias name, join the sys.columns.user_type_id column with types.user_type_id, eg :
select object_name(object_id),
types.name,
*
from sys.columns
inner join sys.types on types.user_type_id =sys.columns.user_type_id
where columns.user_type_id=256
or just
select object_name(object_id),
TYPE_NAME(user_type_id),
*
from sys.columns
where user_type_id=256
UPDATE
I just checked the type's record on a server where I databases with different collations and noticed that the collation changes to match the database's. So even on the same server, using that alias can lead to collation issues

How to check a database in SQL to make sure that all of its tables are in use?

I was assigned to see if all of the current tables in a database are used and if not to drop them. These are the steps I have taken so far:
Searched tables names in the program that uses that database to see if a query has been made in the program based on those tables names.
Investigated if a table primary key has been used in any other places such as view or table (Connectivity with other used tables). I used:
SELECT
t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name
FROM
sys.tables AS t
INNER JOIN
sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE
c.name LIKE 'DeflectionId' -- write the column you search here
ORDER BY
schema_name, table_name;
Searched inside all of the stored procedure texts to see if a table name has been used inside them:
SELECT DISTINCT
o.name AS Object_Name,
o.type_desc
FROM
sys.sql_modules m
INNER JOIN
sys.objects o ON m.object_id = o.object_id
WHERE
m.definition LIKE '%\[Test_Results_LU\]%' ESCAPE '\';
or
SELECT name
FROM sys.procedures
WHERE Object_definition(object_id) LIKE '%Test_Results_LU%'
(from this link: Search text in stored procedure in SQL Server )
Used Object Explorer view to see if a table with the similar/same name and size exists in the database.
Do you think there are other ways that I can use to investigate it better?
Are these steps efficient at all? How would you do it?
Those are all reasonable things to check. One more thing to do would be to turn on profiling or auditing, depending on your SQL server version, and actually monitor for the tables being used for a reasonable time period. You may not be able to do that with a production system, and it's still not 100% guaranteed - what if there's an important table that's only queried once a year?
https://dba.stackexchange.com/questions/40960/logging-queries-and-other-t-sql
https://learn.microsoft.com/en-us/sql/relational-databases/security/auditing/view-a-sql-server-audit-log?view=sql-server-2017
One other suggestion before dropping the tables is to explicitly remove access to them (either with DENY/REVOKE or rename them to table-name_purge) for a week or two and see if anyone complains. If they don't, then it's probably safe to make a backup and then drop them.
A couple of other places to check. Both of these rely on data that is
cached automatically by the system
not persisted between restarts
can be dropped at any time.
so absence from these results does not prove that the table is not used but you may find evidence that a table definitely is in use.
SELECT [Schema] = OBJECT_SCHEMA_NAME(object_id),
[ObjectName] = OBJECT_NAME(object_id),
*
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID()
And in the plan cache
USE YourDB
DROP TABLE IF EXISTS #cached_plans, #plans, #results
DECLARE #dbname nvarchar(300) = QUOTENAME(DB_NAME());
SELECT dm_exec_query_stats.creation_time,
dm_exec_query_stats.last_execution_time,
dm_exec_query_stats.execution_count,
dm_exec_query_stats.sql_handle,
dm_exec_query_stats.plan_handle
INTO #cached_plans
FROM sys.dm_exec_query_stats;
WITH distinctph
AS (SELECT DISTINCT plan_handle
FROM #cached_plans)
SELECT query_plan,
plan_handle
INTO #plans
FROM distinctph
CROSS APPLY sys.dm_exec_query_plan(plan_handle);
WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT cp.*,
st.text,
[Database] = n.value('#Database', 'nvarchar(300)'),
[Schema] = n.value('#Schema', 'nvarchar(300)'),
[Table] = n.value('#Table', 'nvarchar(300)')
INTO #results
FROM #cached_plans cp
JOIN #plans p
ON cp.plan_handle = p.plan_handle
CROSS APPLY sys.dm_exec_sql_text(sql_handle) st
CROSS APPLY query_plan.nodes('//Object[#Database = sql:variable("#dbname") and #Schema != "[sys]"]') qn(n);
SELECT *
FROM #results

I want to find column name in my database - is it possible?

My database name is CARE_DynamicsAX and I want to find a column name workerStatus
select * from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'workerStatus'
If I'm not wrong, you are trying to find the Table where you have a column name as workerStatus. If that is the case, you might run this query to find the same.
This works for the column names from TABLES for SQL Server.
This query would run under the assumption that you know that the column name that you are searching starts with workerStat
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE 'workerStat%'

Locating a table in the database

When I run the following query I get the results as expected.
select * from [Beep].[Bopp]
Then, I ran the query below to "find" the table.
select * from sys.objects
where type='U'
and name like '%Bopp%'
It finds the table and lists the row describing it. However, I can't see any reference to Beep there. The name only contains Bopp, so I'm guessing that there a key that I need to look up but I don't know which column that is nor in what table to look it up.
edit
Based on the comments, I improved the query but I'm still not sure in what table to look up the actual name of the schema. The following gives me waaay to many hits (and setting the type didn't actually give me anything Beep-like.
select * from sys.tables t
left join sys.objects s
on t.schema_id = s.schema_id
where t.name like '%Bopp%'
I checked the objects for the specific name like so.
select * from sys.objects
where name like '%Beep%'
To my surprise, I didn't see any hits at all. Where is the little Beep-y thing hiding?!
You can use the schema_name() function against the schema_id column of sys.objects, like so:
select schema_name([schema_id]) from sys.objects where [name] like '%Bopp%';
Alternatively, you can query INFORMATION_SCHEMA.TABLES, which has a TABLE_SCHEMA column that gives the name of the schema rather than its id.
select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'Bopp';
Beep is the schema name for which you should attach another sys table like so:
select sys.schemas.name as schema_name, sys.objects.name as object_name
from sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type='U'
and sys.objects.name like '%Bopp%'

Resources