How to list all columns in views with relations - SQL Server - sql

I made a lot of views with 7-20 columns in each of them. I need to make a query which will return me the list of tables and columns on which is related every column in every view.
Example of my view:
CREATE VIEW example AS
SELECT t.NAME, t.AGE, t.ADDRESS, p.MOBILE, p.LAPTOP ...
FROM person t, device p
WHERE ...
Query result (needed):
TABLE COLUMN
person NAME
person AGE
person ADDRESS
device MOBILE
device LAPTOP
Is this possible, and how? It saves me a lot of time (there is over 900 columns in all views).
Thanks

Yet you can have this query to pull all the views present in your database along with columns present in them views.
SELECT v.name AS View_Name
,c.name AS Column_Name
FROM sys.views v
INNER JOIN sys.all_columns c ON v.object_id = c.object_id

Here is a more elaborated solution, the idea is to convert views to temp tables with no records (select top 0 * from ...) and for each table retrieve the column names from INFORMATION SCHEMA:
---Create this table First
CREATE TABLE dbo.Views_Columns(
id INT IDENTITY(1,1) PRIMARY KEY,
ViewName varchar(100),
ColumnName varchar(100)
)
--START
DECLARE #CurrentView varchar(100)
DECLARE #CurrentSchema varchar(50)
--Temp table #AllViews stores all views names
SELECT s.name as SchemaName, v.name as ViewName, 0 as Processed
INTO #AllViews
FROM sys.views v INNER JOIN SYS.schemas s ON v.schema_id = s.schema_id
WHILE EXISTS (select * from #AllViews WHERE Processed = 0)
BEGIN
--Clean up our temp table
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp')
BEGIN
DROP TABLE dbo.temp
END
SELECT TOP 1 #CurrentView = ViewName, #CurrentSchema = SchemaName
FROM #AllViews WHERE Processed = 0
EXEC('SELECT TOP 0 * INTO dbo.temp FROM '+#CurrentSchema+'.'+#CurrentView)
INSERT INTO Views_Columns
SELECT #CurrentSchema+'.'+#CurrentView, Column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'temp'
UPDATE #AllViews SET Processed = 1 where ViewName = #CurrentView
END
SELECT * FROM Views_Columns
--END

Related

how to get a select count(x) from a query of table names

I have a query the brings back a list of tables and the counts of those tables.
select *
from error
with a result of
tablename | errorcnt
----------+---------
table1 | 5
table2 | 256
and so on.
I need to do a join so I can get another count from each table as to the records that have been corrected example
select count(fixed)
from table1
so my new result would be
tablename | errorcnt | fixed
----------+----------+------
table1 | 5 | 3
table2 | 256 | 239
and so on.
Without doing a cursor how could I do? I guess a sub query using 'tablename'.
The comment you made:
This is how i populate my errortable SELECT T.name TableName,i.Rows
NumberOfRows FROM sys.tables T JOIN sys.sysindexes I ON T.OBJECT_ID =
I.ID WHERE indid IN (0,1) ORDER BY i.Rows DESC,T.name
Means you are looking for tables, and their respective indexes, for tables that are either a heap (i.e. has no index) or have a clustered index. I'm not sure why this would classify as an "error". I'd expect you to want to look for only heaps. i.e. on where indid = 0. Regardless, I suppose the "fixed" would be to return tables that, for example, didn't have a clustered index which now does. In that case I don't understand the schema and think you have asked a XY Question
With that being said,based off the other comments, you could use derived tables and join on the literal values of error.tablename to prevent the use of a cursor.
select
error.tablename
,error.errorcnt
,fixed = coalesce(t1.ct, t2.ct) --add in for each join.
from
error
left join (select count(fixed) as ct from table1 where fixed = 'Y') as t1 on error.tablename = 'table1'
left join (select count(fixed) as ct from table2 where fixed = 'Y') as t2 on error.tablename = 'table2'
--continue the joins for all values in error.tablename
A cursor would be less code, and dynamic, but you asked for a way without a cursor.
you can use temp table and while loop avoid cursor
DECLARE
#SQLQuery NVARCHAR(100),
#Tablename VARCHAR(100)
CREATE TABLE
#error
(
tablename VARCHAR(100),
errorcnt INT
)
CREATE TABLE
#Table1
(
fixed INT
)
CREATE TABLE
#Table2
(
fixed INT
)
CREATE TABLE
#Temp_fixed
(
fixed INT
)
INSERT INTO
#error
VALUES
(
'#Table1',
5
),
(
'#Table2',
256
)
INSERT INTO
#Table1
VALUES
(
3
)
INSERT INTO
#Table2
VALUES
(
239
)
SELECT
tablename,
errorcnt,
-1 AS fixed
INTO
#Temp_error
FROM
#error
WHILE EXISTS(SELECT TOP 1 1 FROM #Temp_error WHERE fixed = -1)
BEGIN
SET
#Tablename = (SELECT TOP 1 tablename FROM #Temp_error WHERE fixed = -1)
SET
-- #SQLQuery = 'SELECT COUNT(fixed) FROM ' + #Tablename
#SQLQuery = 'SELECT SUM(fixed) FROM ' + #Tablename
INSERT INTO
#Temp_fixed
(
fixed
)
EXECUTE
sp_executesql
#SQLQuery
UPDATE
#Temp_error
SET
fixed = ISNULL((SELECT TOP 1 fixed FROM #Temp_fixed), 0)
WHERE
tablename = #Tablename
TRUNCATE TABLE #Temp_fixed
END
SELECT
tablename,
errorcnt,
fixed
FROM
#Temp_error
DROP TABLE #error
DROP TABLE #Table1
DROP TABLE #Table2
DROP TABLE #Temp_error
DROP TABLE #Temp_fixed

Looping through different tables of different dates

We have a legacy application which created multiple tables with the following naming convention: table_20140618, table_20140623, etc where the date is when the program run. I am trying to clean up the database now, and drop some of these tables.
In each table there are two fields: DateStarted and DateFinished. I want to select the tables (and then drop them) where DateStarted has value and DateFinished is NOT null.
At the moment I am using the following query to select all the tables that start with 'table_'
such as:
Select (TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME LIKE 'table_%';
I am not sure how to get all the tables together by searching within their fields. I could do it through the code, but that should mean multiple hits on the database. Any ideas?
Made this after my first comment above, but you should be able to alter the code to fit your specs. Basically, this will use dynamic SQL to generate the commands based on your filters and conditions. So you can use whatever conditions you want in the SELECT #SQL = ... part, to check for the dates, and then add the table name when the conditions are met.
The script returns a list with tablenames and the drop command, so you can check what you're doing before you do it. But from there you can just copy the drop command list and execute it if you want.
IF OBJECT_ID('tempdb..#TABLES') IS NOT NULL
DROP TABLE #TABLES
CREATE TABLE #TABLES (ROWNMBER INT IDENTITY(1,1), TABLENAME VARCHAR(256) COLLATE DATABASE_DEFAULT)
/*
-- Old code to fetch ALL tables with specified name
INSERT INTO #TABLES
SELECT name
FROM sys.tables
WHERE name LIKE 'table[_]%'
*/
-- Updated code to fetch only those tables which contain the DateStarted and DateFinished columns
INSERT INTO #TABLES
SELECT TAB.name
FROM sys.tables TAB
LEFT JOIN sys.columns C1 on C1.object_id = TAB.object_id
AND C1.name = 'DateStarted'
LEFT JOIN sys.columns C2 on C2.object_id = TAB.object_id
AND C2.name = 'DateFinished'
WHERE TAB.name LIKE 'table[_]%'
AND C1.name IS NOT NULL AND C2.name IS NOT NULL
IF OBJECT_ID('tempdb..#DROPPABLE_TABLES') IS NOT NULL
DROP TABLE #DROPPABLE_TABLES
CREATE TABLE #DROPPABLE_TABLES (TABLENAME VARCHAR(256) COLLATE DATABASE_DEFAULT)
DECLARE #ROW_NOW INT, #ROW_MAX INT, #SQL VARCHAR(MAX), #TABLENAME VARCHAR(256)
SELECT #ROW_NOW = MIN(ROWNMBER), #ROW_MAX = MAX(ROWNMBER) FROM #TABLES
WHILE #ROW_NOW <= #ROW_MAX
BEGIN
SELECT #TABLENAME = TABLENAME FROM #TABLES WHERE ROWNMBER = #ROW_NOW
SELECT #SQL =
'IF (SELECT COUNT(*) FROM '+#TABLENAME+' WHERE DateStarted IS NOT NULL) > 0
AND (SELECT COUNT(*) FROM '+#TABLENAME+' WHERE DateFinished IS NOT NULL) > 0
SELECT '''+#TABLENAME+''''
INSERT INTO #DROPPABLE_TABLES
EXEC(#SQL)
SET #ROW_NOW = #ROW_NOW+1
END
SELECT *, 'DROP TABLE '+TABLENAME DROPCOMMAND FROM #DROPPABLE_TABLES
EDIT:
As per your comment, it seems not all such tables have those columns. You can use the following script to identify said tables and which column is missing, so you can check into them further. And you can use the same idea to filter the results of the first query to only count in tables which have those columns.
SELECT TAB.name TABLENAME
, CASE WHEN C1.name IS NULL THEN 'Missing' ELSE '' END DateStarted_COL
, CASE WHEN C2.name IS NULL THEN 'Missing' ELSE '' END DateFinished_COL
FROM sys.tables TAB
LEFT JOIN sys.columns C1 on C1.object_id = TAB.object_id
AND C1.name = 'DateStarted'
LEFT JOIN sys.columns C2 on C2.object_id = TAB.object_id
AND C2.name = 'DateFinished'
WHERE TAB.name LIKE 'table[_]%'
AND (C1.name IS NULL
OR C2.name IS NULL)

Retrieve SQL View Column Metadata with Custom Query

I created a SQL View and one of the column value is a SQL query for example 'SELECT TOP 1 value from TableB' with the column alias as 'Result'
I tried INFORMATION_SCHEMA.COLUMNS and the COLUMN_NAME only shown 'TableB'. How can I retrieve the 'SELECT TOP 1 value from TableB'?
IronManWK
I found something for you. You should try this :
SELECT M.definition
FROM sys.sql_modules M
WHERE M.object_id = (SELECT V.object_id FROM sys.views V WHERE V.name = 'vue_2')
Reminder, 'vue_2' is into my first answer.
I used SQL SERVER 2012
create table A (
id int identity(1,1),
data nvarchar(255),
)
;
INSERT A (data)
values ('A_ligne1'),('A_ligne2')
create table B (
id int identity(1,10),
data nvarchar(255),
date datetime default GetDate(),
)
;
INSERT B (data)
values ('B_ligne1'),('B_ligne2'),('B_ligne3'),('B_ligne4')
;
GO
CREATE VIEW vue_2
AS
SELECT A.*, (SELECT TOP 1 date FROM B) AS result
FROM A
;
When I make
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'vue_2'
the result set is
TABLE_NAME COLUMN_NAME
vue_2 id
vue_2 data
vue_2 result
So, where is your problem ?

Is there a way to find all invalid columns that are referenced in a view using SQL Server 2012?

I have inherited a large database project with thousands of views.
Many of the views are invalid. They reference columns that no longer exist. Some of the views are very complex and reference many columns.
Is there an easy way to track down all the incorrect columns references?
This answer finds the underlying columns that were originally defined in the views by looking at sys.views, sys.columns and sys.depends (to get the underlying column if the column has been aliased). It then compares this with the data held in INFORMATION_Schema.VIEW_COLUMN_USAGE which appears to have the current column usage.
SELECT SCHEMA_NAME(v.schema_id) AS SchemaName,
OBJECT_NAME(v.object_id) AS ViewName,
COALESCE(alias.name, C.name) As MissingUnderlyingColumnName
FROM sys.views v
INNER JOIN sys.columns C
ON C.object_id = v.object_id
LEFT JOIN sys.sql_dependencies d
ON d.object_id = v.object_id
LEFT JOIN sys.columns alias
ON d.referenced_major_id = alias.object_id AND c.column_id= alias.column_id
WHERE NOT EXISTS
(
SELECT * FROM Information_Schema.VIEW_COLUMN_USAGE VC
WHERE VIEW_NAME = OBJECT_NAME(v.object_id)
AND VC.COLUMN_NAME = COALESCE(alias.name, C.name)
AND VC.TABLE_SCHEMA = SCHEMA_NAME(v.schema_id)
)
For the following view:
create table test
( column1 varchar(20), column2 varchar(30))
create view vwtest as select column1, column2 as column3 from test
alter table test drop column column1
The query returns:
SchemaName ViewName MissingUnderlyingColumnName
dbo vwtest column1
This was developed with the help of this Answer
UPDATED TO RETRIEVE ERROR DETAILS
So this answer gets you what you want but it isn't the greatest code.
A cursor is used (yes I know :)) to execute a SELECT from each view in a TRY block to find ones that fail. Note I wrap each statement with a SELECT * INTO #temp FROM view X WHERE 1 = 0 this is to stop the EXEC returning any results and the 1=0 is so that SQL Server can optimize the query so that it is in effect a NO-OP.
I then return a list of any views whose sql has failed.
I haven't performed lots of testing on this, but it appears to work. I would like to get rid of the execution of each SELECT from View.
So here it is:
DECLARE curView CURSOR FOR
SELECT v.name AS ViewName
FROM sys.views v
INNER JOIN sys.sql_modules m
on v.object_id = m.object_id
OPEN curView
DECLARE #viewName SYSNAME
DECLARE #failedViews TABLE
(
FailedViewName SYSNAME,
ErrorMessage VARCHAR(MAX)
)
FETCH NEXT FROM curView
INTO #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
exec ('SELECT * INTO #temp FROM ' + #viewName + ' WHERE 1=0' )
END TRY
BEGIN CATCH
INSERT INTO #failedViews VALUES (#viewName, ERROR_MESSAGE())
END CATCH
FETCH NEXT FROM curView
INTO #ViewName
END
CLOSE curView
DEALLOCATE curView
SELECT *
FROM #failedViews
An example of an ERROR returned is:
FailedViewName ErrorMessage
--------------- -------------
vwtest Invalid column name 'column1'.
You could use system tables get information.
SELECT v.VIEW_NAME,v.TABLE_CATALOG,v.TABLE_SCHEMA,v.TABLE_NAME,v.COLUMN_NAME
from INFORMATION_SCHEMA.VIEW_COLUMN_USAGE v
left outer join INFORMATION_SCHEMA.COLUMNS c
ON v.TABLE_CATALOG=c.TABLE_CATALOG AND v.TABLE_SCHEMA=c.TABLE_SCHEMA AND v.TABLE_NAME=c.TABLE_NAME AND v.COLUMN_NAME=c.COLUMN_NAME
WHERE c.TABLE_NAME IS NULL
ORDER BY v.VIEW_NAME

Drop table if exist with similar name in two schema

I use this command to drop a table in sql-server 2008
IF EXISTS(SELECT name FROM [DBName]..sysobjects WHERE name = N'TableName' AND xtype='U')
DROP TABLE [DBName].[SchemaName].[TableName];
But now I have 2 tables with same name in different schema:
[DBName].[Schema1].[Members]
And
[DBName].[Schema2].[Members]
So, what is your suggestion for check if exist this tables? How can I check table names with schema?
UPDATE:
OK, there is 3 different answers and all of them worked, so I don't know which one is better, does any one know about use object_id or sys.tables?
IF EXISTS(
SELECT *
FROM [DBName].sys.tables t
JOIN [DBName].sys.schemas s
ON t.SCHEMA_ID = s.schema_id
WHERE
t.name = N'TableName' AND t.type='U'
AND s.NAME = 'SchemaName'
)
DROP TABLE [DBName].[SchemaName].[TableName];
Update:
object_id in sys.tables is the same as object_id in sysobjects for the same table. And is completely the same as function OBJECT_ID returns for the same table name. See the following illustrating examples.
So, you may simplify the query:
IF exists
(
SELECT *
FROM DBName.sys.tables
WHERE object_id = OBJECT_ID('[DBName].[SchemaName].[TableName]')
AND type = 'U'
)
DROP TABLE [DBName].[SchemaName].[TableName];
or in this way:
IF exists
(
SELECT *
FROM DBName.sys.objects
WHERE object_id = OBJECT_ID('[DBName].[SchemaName].[TableName]')
AND type = 'U'
)
DROP TABLE [DBName].[SchemaName].[TableName];
or for sql2000-styled tables:
IF exists
(
SELECT *
FROM DBName..sysobjects
WHERE object_id = OBJECT_ID('[DBName].[SchemaName].[TableName]')
AND xtype = 'U'
)
DROP TABLE [DBName].[SchemaName].[TableName];
Use this:
IF EXISTS
(
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[DBName].[Schema1].[Member]')
AND type in (N'U')
)
PRINT 'Yes'
ELSE
PRINT 'No';
Don't use sysobjects. Use the modern system views in the sys schema (introduced in 2005):
select * from sys.tables
where
schema_id = SCHEMA_ID('Schema1') and
name='tablename'
As soon as you have one "modern" schema in a 2005 or later database, you cannot reliably use sysobjects to match with schemas. If you only have "old" schemas (objects belonging to users and roles), you may be able to query based on user_id.
Wouldn't it be simplest just to:
IF object_id('[schema].[table]') > 0
DROP TABLE [schema].[table]
For non existent tables object_id() returns NULL
For some system tables it returns a negative int