SQL Server view - bad naming convention? - sql

DECLARE #TableName AS VARCHAR(250);
DECLARE #SQL AS VARCHAR(500);
DECLARE #ViewCheck as CURSOR;
SET #ViewCheck = CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'VIEW' AND TABLE_NAME LIKE 'V_WFC%'
OPEN #ViewCheck
FETCH NEXT FROM #ViewCheck INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
Set #SQL = 'SELECT TOP 10 * FROM ' + #TableName
PRINT(#SQL)
EXEC(#SQL)
FETCH NEXT FROM #ViewCheck INTO #TableName;
END
CLOSE #ViewCheck
I have a cursor that runs through all SQL views in a particular schema to sanity check that they continue to function, some are tied to reporting and some used as an application data source in ProSolution.
One of these views is named UnmarkedRegister(Today) the brackets used to differentiate it from a from a similar view, this one is used within an application to drive display data.
While the query runs as expected, returning the correct data - the cursor returns an error
Msg 208, Level 16, State 1, Line 1
Invalid object name 'V_WFC_UnmarkedRegister'
and I'm wondering why the bracketed section is omitted in the EXEC(SQL) section of the cursor?

Use quotename():
Set #SQL = 'SELECT TOP 10 * FROM ' + QUOTENAME(#TableName);

I truly detest cursors and there is no need for a cursor here at all. You can greatly simplify this code in a couple of ways. First I am using sys.views instead of the INFORMATION_SCHEMA views. And then I am using sql to build a dynamic sql string instead of a cursor. Look how simple this can be.
declare #SQL nvarchar(max) = '';
select #SQL = #SQL + 'select top 10 * from ' + QUOTENAME(v.name) + '; select ''('' + convert(varchar(2), ##ROWCOUNT) + '' rows affected'';'
from sys.views v
where v.name LIKE 'V_WFC%'
print #SQL
exec sp_executesql #SQL

Because brackets usually identify a function call
changing this line:
Set #SQL = 'SELECT TOP 10 * FROM ' + #TableName
to this should fix it:
Set #SQL = 'SELECT TOP 10 * FROM [' + #TableName + ']'

Related

Cursors Sub Query - An aggregate may not appear UPDATE statement

I'm very close to gaining understanding how cursors work and using them for small tasks.
i get the following error.
An aggregate may not appear in the set list of an UPDATE statement
I'm guessing my issue here is the UPDATE and the SELECT stamement using an aggregate function such as MAX, but i'm finding it difficult to re-work my quoted statement here. from other posts i've read, its said to use a sub-query?
Base table is built using..
SELECT
QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) AS [DB_Schema],
QUOTENAME(sOBJ.name) AS [TableName],
SUM(sPTN.Rows) AS [Row_Count]
INTO ##tmpRowCount2
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
GROUP BY
sOBJ.schema_id
, sOBJ.name
ORDER BY [Row_Count]
GO
ALTER TABLE ##tmpRowCount2 ADD updated_timestamp datetime NULL;
DECLARE #Row_Count int
DECLARE #sql nvarchar(max)
DECLARE #TableName as VARCHAR(256)
DECLARE #DB_Schema as VARCHAR(256)
DECLARE #updated_timestamp as DATETIME
DECLARE tablenamefromcursor CURSOR FOR
SELECT TableName, Row_Count, DB_Schema
FROM ##tmpRowCount2
OPEN tablenamefromcursor
FETCH NEXT FROM tablenamefromcursor INTO #TableName, #Row_Count, #DB_Schema
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'UPDATE ##tmpRowCount2 SET updated_timestamp = ' +
'(SELECT MAX([updated_timestamp]) FROM ' + #DB_Schema + '.' + #TableName +
') WHERE TableName = ''' + #TableName + ''''
select #sql
EXEC(#sql)
FETCH NEXT FROM tablenamefromcursor INTO #TableName, #Row_Count, #DB_Schema
END
CLOSE tablenamefromcursor
DEALLOCATE tablenamefromcursor
i'm now getting the following errors
Code editied above and i can confirm it works, by debugging with select #sql iwas able to parse the statement and edit the syntax unitl it worked by getting the parethesis and quotes in the right order

Information_schema.columns in the query

I need to pull the data from custom columns in all tables which have "custom1, custom2, custom3....." columns.
Declare #TableName varchar(max)
set #TableName = 'RandomTable';
with main as
(
select distinct
infos.COLUMN_NAME, infos.TABLE_NAME
from
INFORMATION_SCHEMA.COLUMNS infos
where
infos.TABLE_NAME = #TableName
and infos.COLUMN_NAME like 'Custom%%'
)
This query returns the list of custom columns in any table I specify in the parameter. Any idea how to use that in the query so I can get all the data from the RandomTable.Custom%% columns?
Any idea? I'm loosing the plot on it.
Following incomudro idea and guessing you are using SQL Server you could do something like this:
DECLARE
#TableName VARCHAR(MAX)
,#ColumnName VARCHAR(MAX)
,#SQLQuery VARCHAR(MAX)
,#FirstFlag BIT
SET #TableName = 'TEST'
SELECT
COL.COLUMN_NAME
INTO ##CUSTOM_COL
FROM INFORMATION_SCHEMA.COLUMNS COL
WHERE 1 = 1
AND COLUMN_NAME LIKE 'CUSTOM__'
AND TABLE_NAME = #TableName
DECLARE CUR_CUSTOM_COL CURSOR FOR
SELECT * FROM ##CUSTOM_COL
OPEN CUR_CUSTOM_COL
FETCH NEXT FROM CUR_CUSTOM_COL INTO #ColumnName
SET #FirstFlag = 1
SET #SQLQuery = 'SELECT *'
WHILE ##FETCH_STATUS = 0
BEGIN
IF #FirstFlag = 0
BEGIN
SET #SQLQuery = #SQLQuery + ', '
END
SET #SQLQuery = #SQLQuery + #ColumnName
SET #FirstFlag = 0
FETCH NEXT FROM CUR_CUSTOM_COL INTO #ColumnName
END
CLOSE CUR_CUSTOM_COL
DEALLOCATE CUR_CUSTOM_COL
SET #SQLQuery = #SQLQuery + ' FROM ' + #TableName
EXEC(#SQLQuery)
I saved my workspace in this fiddle. Unfortunaly it doesnt work there as intendet, but it should in your SQL Client(or I did a little typing mistake). With some little modifications you could not only display the custom columns from 1 specified table but from all tables. Also you could, besides selecting it, insert the ouput from the custom columns in temp tables.

Iterate Through and Rename All Tables w/ Object Qualifiers MSSQL

I need to iterate through all of the tables that begin with a specific prefix to rename them. The code I've tried is below, but it ends with one of two results, either it crashes SSMS (sometimes), or I get the error message below for each table. I've tried with and with out dbo.
Can anyone tell me what I'm doing wrong or perhaps suggest a better way to do this?
No item by the name of 'dbo.prefix_TableName' could be found in the current database 'DatabaseName', given that #itemtype was input as '(null)'.
Here's the code I'm running...
SET NOCOUNT ON;
USE [DatabaseName];
DECLARE #oq NVARCHAR(5), #tableName NVARCHAR(128), #newTableName NVARCHAR(128);
SET #oq = N'prefix_';
/*
find and rename all tables
*/
DECLARE [tableCursor] CURSOR FOR
SELECT [TABLE_NAME] FROM INFORMATION_SCHEMA.TABLES
WHERE [TABLE_TYPE] = 'BASE TABLE' AND [TABLE_NAME] LIKE #oq + '%'
ORDER BY [TABLE_NAME];
OPEN [tableCursor]
FETCH NEXT FROM [tableCursor] INTO #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #newTableName = REPLACE(#tableName, #oq, N'');
EXEC('EXEC sp_rename ''dbo.' + #tableName + ''', ''' + #newTableName + '''');
END
CLOSE [tableCursor];
DEALLOCATE [tableCursor];
A simpler solution without cursors
declare #oq nvarchar(max) = N'prefix_'
declare #cmd nvarchar(max)
select #cmd = a from (
select 'EXEC sp_rename ''' + TABLE_NAME + ''', ''' + REPLACE(TABLE_NAME, #oq, N'') + ''' '
from INFORMATION_SCHEMA.TABLES
for xml path('')
) t(a)
exec sp_executesql #cmd
In your example nvarchar(5) causes truncation, you probably need nvarchar(7) or nvarchar(max).

Check if View exists before querying it

I want to check if a specific View exists before querying it. I use dynamic SQL to create the query:
DECLARE #sqlCommand varchar(1000)
DECLARE #viewName varchar(1000)
DECLARE #otherDB varchar(1000)
SET #sqlCommand = 'IF EXISTS(SELECT 1 FROM ' + #otherDB + '.sys.views WHERE name=''' + #viewName + ''')
BEGIN
SELECT * FROM ' + #viewName + '
END'
EXEC (#sqlCommand)
So everything works fine as long as #viewName actually exists. However, if #viewName is a View that does not exist in sys.views, I get an error from the compiler:
The OLE DB provider "SQLNCLI11" for linked server "server" does not contain the table #viewName. The table either does not exist or the current user does not have permiossions on that table.
I would have thought that since an IF statement is used, it would just skip the querying of the View. However, seems like the View has to exist otherwise I get the above error.
I've tried alternate solutions, such as using strings for the View names, but no luck. I've also tried the solution in: How to check the existence of a view, but at some point I have to reference the View name in my query, and it would complain
Any info would be greatly appreciated!
Check for the existence of the view outside the dynamic SQL. You are trying to prevent the compile-time error of the view not existing in the select. There is no issue with the if:
IF EXISTS(SELECT 1 FROM sys.views WHERE name = #viewName)
BEGIN
SET #sqlCommand = 'SELECT * FROM ' + #viewName
EXEC(#sqlCommand)
END;
Although it doesn't make a difference in this case, if you are using dynamic SQL, learn about sp_executesql -- it is more powerful than exec() because you can pass variables in and out.
EDIT:
In that case, you essentially have to do dynamic SQL inside dynamic SQL. The following is not tested, so there could be a syntax error:
DECLARE #viewName varchar(1000);
DECLARE #otherDB varchar(1000);
declare #sql nvarchar(max) = '
IF EXISTS (SELECT 1 FROM #otherDB.sys.views WHERE name = ''#viewName'')
BEGIN
DECLARE #sqlCommand nvarchar(max);
SET #sqlCommand = ''SELECT * FROM #viewName'';
EXEC(#sqlCommand);
END;';
SET #sql = replace(replace(#ql, '#otherDB', #otherDB), '#viewName', #viewName);
EXEC(#sql);
What version of SQL Server are you using? I only have SQL Server 2014 available to test with, but the T-SQL below works for both missing and not missing views. I wonder whether the fact that you are checking for existence of the view in otherdb.sys.views but are not qualifying otherdb when selecting from the view is to blame?
declare #viewName varchar(50) = 'MissingView';
declare #sqlCommand nvarchar(1000);
declare #otherdb varchar(20) = 'MyTestDatabase';
set #sqlCommand = N'if exists
(
select 1
from ' + #otherdb + '.sys.views as v
where v.name = ''' + #viewName + '''
)
begin
select * from ' + #otherdb + '.dbo.' + #viewName + ';
end
else
begin
select ''Nah mate - missing view'';
end';
print #sqlCommand;
execute sp_executesql #sqlCommand;
You can use the Else condition when not exists to set error message
DECLARE #sqlCommand varchar(1000)
DECLARE #viewName varchar(1000)
SET #viewName = 'vwName'
SET #sqlCommand = 'IF EXISTS(SELECT 1 FROM sys.views WHERE name=''' + #viewName + ''')
BEGIN
SELECT * FROM ' + #viewName + '
END
ELSE
BEGIN
SELECT ''View not exists''
END
'
EXEC (#sqlCommand)
Beware if your view is in a different schema, because then you need to also check the SCHEMAS table:
SELECT 1 FROM SYS.VIEWS
INNER JOIN SYS.SCHEMAS ON SYS.SCHEMAS.schema_id = SYS.VIEWS.schema_id
WHERE SYS.VIEWS.TYPE='V'
AND SYS.SCHEMAS.NAME=#Your_Schema_Name
AND SYS.VIEWS.NAME=#Your_View_Name

Executing dynamic SQL in a SQLServer 2005 function

I will preface this question by saying, I do not think it is solvable. I also have a workaround, I can create a stored procedure with an OUTPUT to accomplish this, it is just easier to code the sections where I need this checksum using a function.
This code will not work because of the Exec SP_ExecuteSQL #SQL calls. Anyone know how to execute dynamic SQL in a function? (and once again, I do not think it is possible. If it is though, I'd love to know how to get around it!)
Create Function Get_Checksum
(
#DatabaseName varchar(100),
#TableName varchar(100)
)
RETURNS FLOAT
AS
BEGIN
Declare #SQL nvarchar(4000)
Declare #ColumnName varchar(100)
Declare #i int
Declare #Checksum float
Declare #intColumns table (idRecord int identity(1,1), ColumnName varchar(255))
Declare #CS table (MyCheckSum bigint)
Set #SQL =
'Insert Into #IntColumns(ColumnName)' + Char(13) +
'Select Column_Name' + Char(13) +
'From ' + #DatabaseName + '.Information_Schema.Columns (NOLOCK)' + Char(13) +
'Where Table_Name = ''' + #TableName + '''' + Char(13) +
' and Data_Type = ''int'''
-- print #SQL
exec sp_executeSql #SQL
Set #SQL =
'Insert Into #CS(MyChecksum)' + Char(13) +
'Select '
Set #i = 1
While Exists(
Select 1
From #IntColumns
Where IdRecord = #i)
begin
Select #ColumnName = ColumnName
From #IntColumns
Where IdRecord = #i
Set #SQL = #SQL + Char(13) +
CASE WHEN #i = 1 THEN
' Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
ELSE
' + Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
END
Set #i = #i + 1
end
Set #SQL = #SQL + Char(13) +
'From ' + #DatabaseName + '..' + #TableName + ' (NOLOCK)'
-- print #SQL
exec sp_executeSql #SQL
Set #Checksum = (Select Top 1 MyChecksum From #CS)
Return isnull(#Checksum,0)
END
GO
It "ordinarily" can't be done as SQL Server treats functions as deterministic, which means that for a given set of inputs, it should always return the same outputs. A stored procedure or dynamic sql can be non-deterministic because it can change external state, such as a table, which is relied on.
Given that in SQL server functions are always deterministic, it would be a bad idea from a future maintenance perspective to attempt to circumvent this as it could cause fairly major confusion for anyone who has to support the code in future.
Here is the solution
Solution 1:
Return the dynamic string from Function then
Declare #SQLStr varchar(max)
DECLARE #tmptable table (<columns>)
set #SQLStr=dbo.function(<parameters>)
insert into #tmptable
Exec (#SQLStr)
select * from #tmptable
Solution 2:
call nested functions by passing parameters.
You can get around this by calling an extended stored procedure, with all the attendant hassle and security problems.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
Because functions have to play nicely with the query optimiser there are quite a few restrictions on them. This link refers to an article that discusses the limitations of UDF's in depth.
Thank you all for the replies.
Ron: FYI, Using that will throw an error.
I agree that not doing what I originally intended is the best solution, I decided to go a different route. My two choices were to use sum(cast(BINARY_CHECKSUM(*) as float)) or an output parameter in a stored procedure. After unit testing speed of each, I decided to go with sum(cast(BINARY_CHECKSUM(*) as float)) to get a comparable checksum value for each table's data.