Somebody create database with special characters name like 'PRİNCE' long time ago.So we can't change database name anymore.
Because of ' İ ' we can't call database from sys.databases.
select name
from dbo.sysdatabases
where name=cast('PRİNCE' as varchar(100)) COLLATE SQL_Latin1_General_CP1253_CI_AI
it works well
But there is a procedure which we using gives error
'Database 'PRİNCE' does not exist. Make sure that the name is entered correctly.'
DECLARE #dbname VARCHAR(50) -- database name
Declare #strSQL nvarchar(max)
DECLARE c CURSOR FOR
select name from MASTER.dbo.sysdatabases where (name not in('master','model','msdb','tempdb'))
OPEN c;
FETCH NEXT FROM c INTO #dbname;
WHILE ##FETCH_STATUS = 0
begin
select #strSQL='use '+cast(#dbname as varchar(100))COLLATE SQL_Latin1_General_CP1253_CI_AI+' Select Db_name(DB_ID()) as Dbname from sys.database_files ;'
exec sp_executesql #strSQL
fetch next from c into #dbname
end
close c
deallocate c
The problem is your use of varchar throughout and not nvarchar. The specific problem statement is this one:
select #strSQL='use '+cast(#dbname as varchar(100))COLLATE SQL_Latin1_General_CP1253_CI_AI+' Select Db_name(DB_ID()) as Dbname from sys.database_files ;'
I am guessing your database (master I assume) is not in the collation SQL_Latin1_General_CP1253_CI_AI so as soon as the assignment happens, the value is lost.
See, for example, the below:
DECLARE #Database sysname = N'USE ' + 'İ' COLLATE SQL_Latin1_General_CP1253_CI_AI + ';';
SELECT #Database;
This returns USE I; not USE İ;. As object names are a sysname (a synonym of nvarchar(128) NOT NULL) then make sure you use that data type through your code:
USE master;
GO
DECLARE #dbname sysname; -- database name
DECLARE #strSQL nvarchar(max);
DECLARE c CURSOR FOR
SELECT [name]
FROM sys.databases --Don't use the old objects!
WHERE ([name] NOT IN(N'master',N'model',N'msdb',N'tempdb'));
OPEN c;
FETCH NEXT FROM c INTO #dbname;
WHILE ##FETCH_STATUS = 0 BEGIN
SET #strSQL = N'USE ' + QUOTENAME(#dbname) + N'; SELECT DB_NAME(DB_ID()) AS DBName from sys.database_files;'; --Note QUOTENAME as well for proper quoting.
EXEC sys.sp_executesql #strSQL;
FETCH NEXT FROM c INTO #dbname;
END;
CLOSE c;
DEALLOCATE c;
Ideally, I'd get rid of the CURSOR as well, but that's a completely different question.
Related
I am newbie here. I have many databases in my SSMS, so I need to find the database name using the given table name using stored procedures.
And I am not good at writing SP's and handling errors.
I apologize for my English.
Thank you
I tried it using cursors in stored procedure.
But I am getting errors as I am not good at handling errors.
You could create the stored procedure in the following:
CREATE PROCEDURE sp_Get_Tables
#schema VARCHAR(50) = 'dbo',
#table_name VARCHAR(100) = 'Default_Table_Name'
AS
SELECT name
FROM sys.databases
WHERE CASE WHEN state_desc = 'ONLINE' THEN OBJECT_ID(QUOTENAME(name) + '.' + #schema + '.' + #table_name, 'U') END IS NOT NULL
And execute the stored procedure you can in the following:
EXEC sp_Get_Names 'Schema', 'Table_Name'
Try This:
Create PROCEDURE Pro_FindTable
(#tableName VARCHAR(MAX))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #name VARCHAR(MAX),
#dbid INT;
DECLARE C CURSOR FAST_FORWARD FOR(
SELECT name,
database_id
FROM sys.databases);
OPEN C;
FETCH NEXT FROM C
INTO #name,
#dbid;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #query NVARCHAR(MAX)
= 'IF EXISTS(SELECT name FROM(SELECT name, COUNT(*)Over(Order By (Select Null)) as RN FROM(SELECT '''
+ #name + ''' AS name UNION ALL SELECT name FROM [' + #name
+ '].sys.tables WHERE type=''U'' AND name = ''' + #tableName
+ ''') as K)as K Where RN>1)
Select '''+ #name + '''';
EXEC (#query);
FETCH NEXT FROM C
INTO #name,
#dbid;
END;
CLOSE C;
DEALLOCATE C;
END;
And call it like this:
EXEC Pro_FindTable 'MyTable'
Result will be all databases which has a table named 'MyTable'
I wanted to monitor sizes of any databases in an SQL Server instance so I came up with this query that builds a temporary table which delivers what I want
/*DROP TEMP TABLE IF STILL EXISTENT*/
IF OBJECT_ID('tempdb..#sizes') IS NOT NULL
DROP TABLE #sizes;
/*DECLARE CURSOR curs1 to query the names of all relevant databases*/
DECLARE curs1 INSENSITIVE CURSOR FOR
select CAST(name AS varchar(MAX)) from sys.databases WHERE state_desc ='ONLINE' AND name NOT IN ('tempdb', 'master', 'model', 'msdb', 'sysdb', 'tempdb2')
/*Open Cursor*/
OPEN curs1
/*Declare Variable to hold the value of current db*/
declare #dbname varchar(MAX) = 'dummy'
/*Create the temporary table for results*/
CREATE TABLE #sizes (DBname varchar(MAX), physicalname varchar(MAX), TotalSizeMB INT, AvailableSpaceMB INT)
/*Query the datafile statistics for every database dynamically*/
WHILE(1=1)
BEGIN
FETCH NEXT FROM curs1 INTO #dbname
IF ##FETCH_STATUS != 0
BREAK;
EXECUTE('USE ' + #dbname + ';INSERT INTO #sizes
SELECT DB_NAME(), f.physical_name,
CAST((f.size/128.0) AS DECIMAL(15,2)),
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, ''SpaceUsed'') AS int)/128.0 AS DECIMAL(15,2))
FROM sys.database_files f;')
END
Select * from #sizes
/*Cleanup*/
DROP TABLE #sizes
CLOSE curs1
DEALLOCATE curs1
When the cursor fetches a string containing a "-", it splits the string at that point, leaving an incomplete database name, which can not be found. I isolated the EXECUTE function to be the problem. I assume this has to do with the change of of context when changing the database with the use command. But I also tried the datatypes nvarchar, text and ntext (which are not valid for local variables), char and nchar as well as sysname, which is the fieldtype of the sys.databases' "name"-column.
Any suggestions on how to stop this behaviour?
Try escaping the database name:
'USE [' + #dbname + ']; ...
Here is a quick example of how you can do this without using a cursor. It still requires dynamic sql but no cursor. :) I realize you are using FILEPROPERTY which will be NULL here but I tossed this example together quickly. And since I suspect you will probably continue to use the cursor version you already have I didn't refactor that piece.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL +
'SELECT DatabaseName = ''' + name + ''', f.physical_name collate SQL_Latin1_General_CP1_CI_AS,
CAST((f.size/128.0) AS DECIMAL(15,2)),
CAST(f.size/128.0 - CAST(FILEPROPERTY(f.name, ''SpaceUsed'') AS int)/128.0 AS DECIMAL(15,2))
FROM [' + name + '].sys.database_files f union all '
from sys.databases
WHERE state_desc ='ONLINE'
AND name NOT IN ('tempdb', 'master', 'model', 'msdb', 'sysdb', 'tempdb2')
select #SQL = left(#SQL, len(#SQL) - 10)
select #SQL
--Uncomment the next line when you are comfortable the dynamic sql will work
--exec sp_executesql #SQL
I'm using a linked server and finding it very painful to write queries like this:
select * from [10.150.10.109].lhf.[dbo].[TABLE_NAME]
Is it possible to use a synonym for something like this:
CREATE SYNONYM [DataRelay] FOR [10.150.10.109].[lhf].[dbo]
in order to be able to query like this:
select * from DataRelay.TABLE_NAME
Without the capabilities of Intellisense, this is just painful...
No, there is no short hand for linked servers, however, you can alias tables in your queries to make it a bit easier.
select * from [10.150.10.109].lhf.[dbo].[TABLE_NAME] T
WHERE
T.FieldName=1
OR
T.FieldName=2
Now that I had a minute what I was saying in my comment is that you cannot create a synonym for just part of an object path as you desire. But you can dynamically script the drop and creation of synonyms for any object in your remote database pretty easily. here is an example of how to do if for user tables. For other objects you can use the sys.objects instead of sys.table system view.
Technique key words for more learning. Dynamic SQL, cursor, schema views.
DECLARE #ServerAndDB SYSNAME = '[10.150.10.109].[lhf]'
DECLARE #SynonymSchema SYSNAME = '[syn]'
DECLARE #ObjectPath NVARCHAR(1000)
DECLARE #SynonymName NVARCHAR(1000)
DECLARE CursorName CURSOR FOR
SELECT
#ServerAndDB + QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) as ObjectPath
,#SynonymSchema + '.' + QUOTENAME(t.name) as SynonymName
FROM
[10.150.10.109].[lhf].sys.tables t
WHERE
t.type = 'U'
OPEN CursorName
FETCH NEXT FROM CursorName
INTO #ObjectPath, #SynonymName
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
DECLARE #SQL NVARCHAR(MAX)
IF EXISTS (SELECT * FROM sys.synonyms WHERE object_id = OBJECT_ID(#SynonymName))
BEGIN
SET #SQL = 'DROP SYNONYM ' + #SynonymName
EXECUTE sp_executesql #SQLString
SET #SQL = ''
END
SET #SQL = 'CREATE SYNONYM ' + #SynonymName + ' FOR ' + #ObjectPath
EXECUTE sp_executesql #SQLString
SET #SQL = ''
END TRY
BEGIN CATCH
--Can do error handling here
END CATCH
FETCH NEXT FROM CursorName
INTO #ObjectPath, #SynonymName
END
CLOSE CursorName
DEALLOCATE CursorName
I have a query that I want to be able to use across database schemas. Right now it is written so that I need to replace the schema in several places of the query. How do I set that up as a variable so that all I need to do is change it in one place?
You can do this with Dynamic SQL:
DECLARE #sql VARCHAR(MAX)
,#schema VARCHAR(255) = 'dbo'
SET #sql = 'SELECT *
FROM '+#schema+'.yourTable
'
EXEC (#sql)
You could use this in a cursor to loop through schema's:
DECLARE #Iterator varchar(255)
,#strSQL varchar(MAX)
DECLARE xyz CURSOR
FOR
--Select stuff to iterate over
SELECT name
FROM sys.schemas
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
--Do stuff
SET #strSQL = 'SELECT *
FROM '+#Iterator+'.yourTable
'
Exec (#strSQL)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
To test your dynamic SQL statements, you can change EXEC to PRINT and ensure that the resulting query is as you intended.
Synonyms might be the answer to your question.
You can use dynamic SQL. Here is an example -
DECLARE #Column varchar(25)
DECLARE #sqlStmt varchar(max)
SET #Column = 'MyColumn'
SET #sqlStmt = N'SELECT ' + #Column + ' FROM MyTable'
EXEC (#sqlStmt)
I have the following code (cursor):
DECLARE #SN VARCHAR(20);
DECLARE #sql NVARCHAR(MAX);
DECLARE C CURSOR LOCAL FAST_FORWARD
FOR SELECT DISTINCT(SERVERNAME) FROM INSTALLATION
where DATABASETYPE = 'MsSql' AND SERVERNAME IN ('x');
OPEN C;
FETCH NEXT FROM C INTO #SN;
WHILE (##FETCH_STATUS = 0)
BEGIN
PRINT #SN;
-- you could loop here for each database, if you'd define what that is
SELECT name
FROM master.dbo.sysdatabases
WHERE name not in ('master','model','msdb','tempdb');
SET #sql = N'SELECT TOP 1 NAME FROM TABLE ';
EXEC sp_executesql #sql;
FETCH NEXT FROM C INTO #SN;
END
CLOSE C;
DEALLOCATE C;
I would like to be able to loop through every server and execute a select statement on some (not all) of their databases.
The query is something like:
SELECT TOP 1 NAME FROM TABLE
The server from where I am running the cursor has all the others as linked servers.
DECLARE #SN VARCHAR(20);
DECLARE C CURSOR LOCAL FAST_FORWARD
FOR SELECT DISTINCT(SERVERNAME) FROM TABLE
where SERVERNAME NOT IN ('SRV1','SRV2','SRV3');
OPEN C;
FETCH NEXT FROM C INTO #SN;
WHILE (##FETCH_STATUS = 0)
BEGIN
PRINT #SN;
-- you could loop here for each database, if you'd define what that is
SET #sql = N'SELECT * FROM ' + #SN + '.master.dbo.TABLE;';
EXEC sys.sp_executesql #sql;
FETCH NEXT FROM C INTO #SN;
END
CLOSE C;
DEALLOCATE C;
Changes:
There is no reason to use the default cursor options here - global, updatable, dynamic, scrollable, etc. Background.
As a habit / best practice, use sp_executesql and not EXEC(). While it doesn't really matter in this case, it can matter in others, so I'd prefer to always code the same way. Background.
Also, please get in the habit of terminating your statements with semi-colons. You'll have to, eventually. Background.
EDIT
Now that we have a little more information about your actual requirements, I suggest this bit of code. Oh, and look, no cursors (well, no explicit cursor declarations and all the scaffolding that comes with them)!
SET NOCOUNT ON;
DECLARE #dbs TABLE(SERVERNAME SYSNAME, DBNAME SYSNAME);
DECLARE #sql NVARCHAR(MAX) = N'';
-- first, let's get the databases on each server:
SELECT #sql += N'SELECT ''' + SERVERNAME + ''', name FROM '
+ QUOTENAME(SERVERNAME) + '.master.sys.databases
WHERE database_id > 4
AND name NOT IN (N''somedb'',N''someotherdb'');'
FROM dbo.INSTALLATION
WHERE DATABASETYPE = 'MsSql'
AND SERVERNAME IN ('x');
INSERT #dbs EXEC sys.sp_executesql #sql;
SELECT #sql = N'';
-- now, build a command to run in each database context:
SELECT #sql += N'
EXEC ' + QUOTENAME(SERVERNAME) + '.'
+ QUOTENAME(DBNAME) + '.sys.sp_executesql #sql;'
FROM #dbs;
-- feel free to change the 3rd parameter here:
EXEC sys.sp_executesql #sql, N'#sql NVARCHAR(MAX)',
N'SELECT ##SERVERNAME, DB_NAME(), actual_columns FROM dbo.table_name;';
This will fail if table_name doesn't exist, so you may still have some work to do if you want to facilitate error handling. But this should get you started.
Also, please be conscious of, and consistently use, the schema prefix. Background.