I have sql scripts DROP ... CREATE... for views, procedures, triggers, functions (in files on disk). I need to run this scripts to update database structure.
But order of executing scripts is important. Because if view1 depends on view2, and I run scripts for view1 first, an error may occurs.
Let's suppose that there are no adscititious dependencies in my scripts, so database knows about all dependencies.
Is there a way to select all this objects names from sql server in dependency order? So I can run scripts in this order and not afraid about above errors.
I wrote this sql:
SET NOCOUNT ON
declare #deps TABLE (name nvarchar(512), dep_name nvarchar(512))
declare #ordered TABLE (name nvarchar(512), [level] INT)
insert #deps(name, dep_name)
SELECT DISTINCT CAST(OBJ.name as nvarchar(512)) AS ObjectName,
CAST(REFOBJ.name as nvarchar(512)) AS ReferencedObjectName
FROM sys.sql_dependencies AS DEP
INNER JOIN
sys.objects AS OBJ
ON DEP.object_id = OBJ.object_id
INNER JOIN
sys.schemas AS SCH
ON OBJ.schema_id = SCH.schema_id
INNER JOIN sys.objects AS REFOBJ
ON DEP.referenced_major_id = REFOBJ.object_id
INNER JOIN sys.schemas AS REFSCH
ON REFOBJ.schema_id = REFSCH.schema_id
LEFT JOIN sys.columns AS REFCOL
ON DEP.class IN (0, 1)
AND DEP.referenced_minor_id = REFCOL.column_id
AND DEP.referenced_major_id = REFCOL.object_id
WHERE OBJ.type_desc IN ('VIEW','SQL_STORED_PROCEDURE','SQL_INLINE_TABLE_VALUED_FUNCTION','SQL_TRIGGER','SQL_SCALAR_FUNCTION')
AND REFOBJ.type_desc IN ('VIEW','SQL_STORED_PROCEDURE','SQL_INLINE_TABLE_VALUED_FUNCTION','SQL_TRIGGER','SQL_SCALAR_FUNCTION')
insert #ordered(name, [level])
select distinct d1.dep_name, 1 as [level]
from #deps d1
LEFT JOIN #deps d2 ON d1.dep_name = d2.name
where d2.name IS NULL
WHILE EXISTS(select * from #deps)
BEGIN
delete d
FROM #deps d
JOIN #ordered o ON d.name = o.name
insert #ordered(name, [level])
SELECT DISTINCT d0.name, (select MAX([level]) + 1 FROM #ordered)
FROM #deps d0
LEFT JOIN (
SELECT d.name
from #deps d
LEFT JOIN #ordered o ON d.dep_name = o.name
WHERE o.name IS NULL
) dfilter ON d0.name = dfilter.name
WHERE dfilter.name is NULL
END
select * from #ordered
ORDER by level asc
Related
we have a Table with a list of table names we want to be created. They don't have an ID column or anything, it's just a few rows of data with 2 columns. Thing is we want to merge that table with Information_schema.table to check which of the tables we have already created and which we have not, so we wrote the query below as a temp to achieve such:
with cte1 as (
select d.TABNAME, d.CLASS from dbo.table_list as d
left join INFORMATION_SCHEMA.TABLES as t on t.TABLE_NAME = d.TABNAME
where d.CLASS in ('INIT','STERN') and table_schema = 'dbo'),
cte2 as (select d.TABNAME, d.CLASS
from dbo.table_list as d
where d.CLASS in ('INIT','TERN') and d.TABNAME not in (select [TABLE NAME] from cte1))
select *, 'Active' as [Status] from cte1 union all
select * , 'Inactive' from cte2
This is what table_list looks like:
TABNAME
CLASS
TABLE1
INIT
TABLE2
STERN
TABLE3
STERN
TABLE4
STERN
TABLE5
INIT
We already have TABLE1 and TABLE2 created so the result of the query looks like this:
TABNAME
CLASS
STATUS
TABLE1
INIT
Active
TABLE2
STERN
Active
TABLE3
STERN
Inactive
TABLE4
STERN
Inactive
TABLE5
INIT
Inactive
It works well enough like this but we were wondering if we could make it shorter.
This can be way shorter, yes. You could just reference the table dbo.table_list and see if you get a valid OBJECT_ID:
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN OBJECT_ID(N'dbo.' + QUOTENAME(tl.TABNAME)) IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
WHERE tl.CLASS IN ('INIT','STERN');
If you wanted to use the catalog views, you could use CROSS APPLY to join to the table while supplying a value for both the schema and table name, or just JOIN to sys.schemas based on a literal and then LEFT JOIN to sys.tables:
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN st.[name] IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
CROSS APPLY (SELECT t.[name]
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.[name] = N'dbo'
AND t.[name] = tl.TABNAME) st
WHERE tl.CLASS IN ('INIT','STERN');
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN t.[name] IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
JOIN sys.schemas s ON s.[name] = N'dbo'
LEFT JOIN sys.tables t ON s.schema_id = t.schema_id
AND tl.TABNAME = t.[name]
WHERE tl.CLASS IN ('INIT','STERN');
A colleague asked me to help them to identify the views in a database that have one or more computed columns. The database has hundreds of views so they're trying to find an automated way to accomplish this task. I am not seeing the results in the database that I was expecting. Here is an example:
--DROP TABLE dbo.Products
CREATE TABLE dbo.Products
(
ProductID int IDENTITY (1,1) NOT NULL
, QtyAvailable smallint
, UnitPrice money
);
--DROP VIEW dbo.uvw_Products
CREATE VIEW dbo.uvw_Products
AS
SELECT ProductID
, QtyAvailable
, UnitPrice
, (QtyAvailable * UnitPrice) AS InventoryValue
FROM dbo.Products;
-- Look at the view and find the computed column
SELECT OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name],
TY.[name] AS system_data_type, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded], AC.[is_computed]
FROM sys.[views] AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id] AND AC.[user_type_id] = TY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
AND T.[name] = 'uvw_Products'
ORDER BY T.[name], AC.[column_id]
-- Pulls up no results - no entries in sys.computed_columns
SELECT TOP 10 *
FROM sys.computed_columns C
INNER JOIN sys.views V ON C.[object_id] = V.[object_id]
WHERE V.[name] = 'uvw_Products'
As you can see from this simple example, SQL Server does not seem to be storing the value in the is_computed column.
What am I missing? How can we find the computed columns in views?
I know it's not ideal, I don't think a fool-proof solution exists for this problem, but depending on your naming conventions you could do a join to the tables sys view on column names so see which columns exist and don't exist in there.
for example:
SELECT v.*
FROM
(
SELECT [Schema] = OBJECT_SCHEMA_NAME(t.[object_id], DB_ID())
, [table_name] = t.[name]
, [column_name] = vc.[name]
FROM sys.[views] t
JOIN sys.[all_columns] vc ON t.[object_id] = vc.[object_id]
) v
LEFT JOIN
(
SELECT [Schema] = OBJECT_SCHEMA_NAME(t.[object_id], DB_ID())
, [table_name] = t.[name]
, [column_name] = vc.[name]
FROM sys.[tables] t
JOIN sys.[all_columns] vc ON t.[object_id] = vc.[object_id]
) t
ON t.[column_name] = v.[column_name]
WHERE t.[table_name] IS NULL
Returns:
Schema | table_name | column_name
----------------------------------------
dbo | uvw_Products | InventoryValue
And if your views contain the source table in its name like 'uvw_Products' you could also use that in your join to avoid columns in other tables getting in the way.
Again its not ideal but a relatively simple solution to narrow the search
I am looking to write a query that will return all of the stored procedures and views that reference a specific database that I am looking to rebuild. I also need the query to return the columns and table names that the returned stored procedures / views contain in reference to the database being rebuilt.
I have a query right now that will return the stored procs and views, but have been unsuccessful in returning the column and table names that reference the database.
SELECT Distinct so.Name, so.type--, sc.text
FROM sysobjects so(NOLOCK)
INNER JOIN syscomments sc (NOLOCK) on so.Id = sc.ID
WHERE so.Type in( 'p' , 'v')
AND sc.Text LIKE '%membership_dw.%'
ORDER BY so.Name
Membership_DW is the name of the database where the stored procedures and views are currently stored, and it is also the database that is being recreated.
Here is a quick and dirty query to do it
;WITH cte as(
select *
from sys.sysdepends t1
inner join sys.objects t2 on t1.depid = t2.object_id
where t2.type = 'U' and is_ms_shipped = 0)
SELECT t5.name as SP_NAME, t6.name as TBL_NAME, t9.name COLNAME from sys.objects t5
INNER JOIN cte t6 on t6.id = t5.object_id
INNER JOIN sys.columns t9 on t6.object_id = t9.object_id
where t5.is_ms_shipped = 0 and t6.name not like 'sys%'
order by sp_name, TBL_NAME
I'm trying to insert the result from a query into a new table.
I'm using this query and want to gather the result into a single table.
The query (I found somewhere) looks like this:
USE [AdventureWorksDW2012]
SELECT
OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name],
AC.[name] AS [column_name],
TY.[name] AS system_data_type,
AC.[max_length],
AC.[precision], AC.[scale],
AC.[is_nullable], AC.[is_ansi_padded]
FROM
sys.[tables] AS T
INNER JOIN
sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN
sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id]
AND AC.[user_type_id] = TY.[user_type_id]
WHERE
T.[is_ms_shipped] = 0
ORDER BY
T.[name], AC.[column_id];
Try using an INTO clause like this:
USE [AdventureWorksDW2012]
SELECT OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name],
TY.[name] AS system_data_type, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded]
INTO dbo.MyNewTable
FROM sys.[tables] AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id]
AND AC.[user_type_id] = TY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
ORDER BY T.[name], AC.[column_id]
;
Use INTO clause as next:-
USE [AdventureWorksDW2012]
SELECT OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name],
TY.[name] AS system_data_type, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded]
Into New_table -- this line is added.
FROM sys.[tables] AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id] AND AC.[user_type_id] = TY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
ORDER BY T.[name], AC.[column_id]
The sample code is
Select *
Into New_table
From Exist_Table
and as MSDN says:-
SELECT…INTO creates a new table in the default filegroup and inserts
the resulting rows from the query into it.
I need to be able to join to a different database (on the same server), and the name of the database I need to join to is dependent on the record being examined. I've got something along the lines of
SELECT *
FROM orders o
INNER JOIN orderdetail od ON od.okey = o.okey
INNER JOIN scheduledetail scheddet on scheddet.odkey = od.odkey
INNER JOIN schedules sched on sched.schedid = scheddet.schedid
INNER JOIN databases db on db.dbdate = sched.releasedate
INNER JOIN [db.dbname].detail det on det.odkey = od.odkey
However, as far as I can tell transact SQL won't allow this kind of join syntax. Any ideas?
If all the detail tables have the same columns, you could create a view that wraps all of them.
DECLARE #sql varchar(max);
SELECT
#sql = COALESCE(#sql + ' UNION ALL ','CREATE VIEW unified_details AS ')
+ 'SELECT *,'+QUOTENAME([dbname])+' AS [dbname] FROM '+QUOTENAME([dbname])+'.[dbo].[detail]'
FROM databases;
EXEC sp_executesql #sql;
SELECT *
FROM orders o
INNER JOIN orderdetail od ON od.okey = o.okey
INNER JOIN scheduledetail scheddet on scheddet.odkey = od.odkey
INNER JOIN schedules sched on sched.schedid = scheddet.schedid
INNER JOIN databases db on db.dbdate = sched.releasedate
INNER JOIN unified_details det on det.odkey = od.odkey and db.dbname = det.dbname