Check: does table exist in SQL Server database? - sql-server

I have an installation script containing the following:
IF NOT EXISTS(
SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_CATALOG = 'mydatabasename'
AND TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'sometablename'
AND TABLE_TYPE = 'BASE TABLE'
)
CREATE TABLE dbo.sometablename ...
Problem is that
I have to allow the user to select a different database name
I cannot use any placeholders ("variables") in the script.
the database server may of course contain a different database which already contains a table sometablename
The installer does a USE mydatabasename before the script is executed; and the installer allows to select a variable database name for this. However, I can't use that variable inside the script, because every replacement in the SQL script is already done at build time of the installer.
So, how can I check whether the used database already contains table sometablename?

Just remove TABLE_CATALOG from your query. All tables in the INFORMATION_SCHEMA.TABLES view belong to the current database.

You may select the name of the actual database in use and replace it in an execute statement.
declare #dbname varchar(50) -- Adjust to your longer database name length.
set #dbname = db_name()
if not exists (
select 1
from information_schema.tables
where table_catalog = #dbname
and table_schema = 'dbo'
and table_name = 'someTableName'
and table_type = 'base table'
) begin
execute (
'create table ' + #dbname + '.dbo.someTableName (
someFieldName someDataType
...
)'
)
end
Otherwise, it always uses the current database so that it considers the database currently in used selected through your use statement.
SQL SERVER – Get Current Database Name

You can still use the SQL Script replacement support from Advanced Installer.
Indeed, the replacement is done at build time, but the properties referenced are resolved at install time, so you can use the database name selected by the user. Just set the field "Replace with" correctly: [SELECTED_DB]
The replacement in the script is done at build time because you cannot write directly the properties as formatted in the script (syntax restrictions).

Related

How to drop a database if a variable with its logical name exists in SQL Server?

I created a variable containing the logical name of a database, and drop it if this is present in my LocalDB.
The problem that I am facing in my current code is that it only searches in the physical names of the existing databases.
(I have a database with the logical name 'Development' which does not get removed).
DECLARE #dbname nvarchar(128)
SET #dbname = 'Development'
IF(EXISTS(SELECT NULL FROM sys.databases
WHERE ('[' + name + ']' = #dbname OR name = #dbname )))
BEGIN
DECLARE #sql VARCHAR(MAX)
SELECT #sql = COALESCE(#sql,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#dbname) AND SPId <> ##SPId
EXEC(#sql)
EXEC('DROP DATABASE ' + #dbname);
PRINT 'Existing database removed'
END
ELSE
BEGIN
PRINT 'Database not present';
END
The result I get is the print 'Database not present' and the db is not removed.
What I would like is a deleted database (.mdf and .ldf)
The user executing the query probably does not have permissions to see the database.
If the caller of sys.databases is not the owner of the database and
the database is not master or tempdb, the minimum permissions required
to see the corresponding row are ALTER ANY DATABASE or the VIEW ANY
DATABASE server-level permission, or CREATE DATABASE permission in the
master database. The database to which the caller is connected can
always be viewed in sys.databases.
Taken from https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql?view=sql-server-2017
Try running the below query in SSMS to check if you can see your database.
SELECT * FROM sys.databases
The database doesn't have the logical name "development". One of the files it contains has that logical name.
To get the database name corresponding to the logical file name you can use
SELECT DB_NAME(database_id)
FROM sys.master_files
WHERE name = 'Development'
And then assign that to #dbname - there may be multiple results. There is no guarantee that logical file name is unique across databases

Running a procedure in all databases

I've created a system stored procedure in the Master database which can be run in all databases. I want to run it in all databases at once, here what I use:
use Master
GO
declare #sql nvarchar(1000)
SET #sql = 'USE [?]; EXEC [dbo].[sp_procedure]'
EXEC sp_MSforeachdb #sql
The problem is - not all databases have have similar table and column structure. So inside procedure, I do this before doing any calculations:
if exists(select 1
from
INFORMATION_SCHEMA.TABLES b,
INFORMATION_SCHEMA.COLUMNS c
where
b.TABLE_TYPE = 'BASE TABLE'
and b.TABLE_NAME = 'tablename'
and c.TABLE_NAME = b.TABLE_NAME
and c.COLUMN_NAME = 'columnname')
So, if while running across all databases, there is no table named 'tablename' with column name 'columnname' it should skip procedure for that database and go on to the next database. I have a database which has 'tablename' table, but doesn't have 'columnname' column in this table, and it returns this error:
Invalid column name 'columnname'.
Why that if exists statement goes inside if statement? Shouldn't it terminate as soon as condition is not met? How can I handle this situation?
That is because SQL Engine tries to compile the code, so it checks the code inside IF block.
You have to use that comparison outside the stored procedure, e.g. in your SET #sql = '......' command, but it will not run that SP if the column does not exist.
So you can try something else - build the query (nvarchar variable) inside the SP and execute it as dynamic SQL.

Declaring Databasename in sql query

I am preparing a group of sql queries for a dashboard. I want to declare the database name at the beginning so that the queries will work on the database specified on top without making any changes in the underlying code
Original query:
SELECT *
FROM Check.dbo.Dates_table
The query I want:
DECLARE #Databasename VARCHAR(200)
SET #Databasename = 'Check.dbo'
SELECT * FROM #Databasename.Dates_table
You can use "USE" operator: https://msdn.microsoft.com/en-AU/library/ms188366.aspx
use Check
SELECT * FROM dbo.Dates_table

Remove user name from table name

I restored a database after a server failure and now I'm running into a problem where the table names show as database_user_name.table_name. So when I query something like:
select * from contacts
it doesn't work because it expects it be fully qualified, as in:
select * from user1000.contacts
The problem with this is that I have hundreds of stored procedures that reference the tables with their name, so none of the queries work.
Is there a way to tell SQL Server 2005 to drop the username from the table without changing the user as the owner?
try this advice from the manual:
To change the schema of a table or view by using SQL Server Management Studio, in Object Explorer, right-click the table or view and then click Design. Press F4 to open the Properties window. In the Schema box, select a new schema.
If you are sure none of the tables exist in the dbo schema as well, then you can say:
ALTER SCHEMA dbo TRANSFER user1000.contacts;
To generate a set of scripts for all of the tables in that schema, you can say:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'
ALTER SCHEMA dbo TRANSFER user1000.' + QUOTENAME(name) + ';'
FROM sys.tables
WHERE SCHEMA_NAME([schema_id]) = N'user1000';
PRINT #sql;
--EXEC sp_executesql #sql;
(Once you're happy with the PRINT output - acknowledging that it will be truncated at 8K even though the variable really contains the whole script - uncomment the EXEC and run it again. This does not check for potential conflicts.)
But the real fix is to fix your code. You should never say select * from contacts - both the * and the missing schema prefix can be problematic for various reasons.

How to export a SQL Server 2008 Database Diagram to another DB?

I use the handy Database Diagramming tool in SQL Server 2008 for creating and managing relationships. I have exported the sourceDB to the destinationDB but the diagram doesn't come across.
I am looking around trying to figure out how to export just the diagram I have in one database to another... This online KB article fails since select * from dtproperties doesn't exist anymore.
#Ash I was having the same problem. Here's what we did to get around it...
It seems that System Diagrams are stored within the "sysdiagrams" table. So the first thing you need to do is determine the diagram_id of the Diagram you wish to copy. Run the following query to list them all. ** Note you need to replace "SourceDB" with the name of your database.
-- List all database diagrams
SELECT * FROM [SourceDB].[dbo].sysdiagrams
Then you can use INSERT to duplicate the diagram from one database to another as follows. ** Note again replace "SourceDB" with the name of the Database containing the existing diagram and "DestinationDB" with the name of the Database you wish to copy to. Also #SourceDiagramId should be set to the id retrieved above.
-- Insert a particular database diagram
DECLARE #SourceDiagramId int = 1
INSERT INTO [DestinationDB].[dbo].sysdiagrams
SELECT [name],diagram_id , version,definition from [SourceDB].[dbo].sysdiagrams
WHERE diagram_id = #SourceDiagramId
Then you need to set the "principal_id" to 1 manually.
-- Update the principal id (no idea why, but it set the owner as some asp_net user
UPDATE [DestinationDB].[dbo].sysdiagrams
SET principal_id = 1
This worked for us it seems pretty hacky especially since the Diagram is stored entirely in a single binary field "definition".
Answer comes from:
http://www.dotnetspider.com/resources/21180-Copy-or-move-database-digram-from-for.aspx
This generates an import string:
SELECT
'DECLARE #def AS VARBINARY(MAX) ; ' +
'SELECT #def = CONVERT(VARBINARY(MAX), 0x' + CONVERT(NVARCHAR(MAX), [definition], 2) + ', 2) ;' +
' EXEC dbo.sp_creatediagram' +
' #diagramname=''' + [name] + ''',' +
' #version=' + CAST([version] AS NVARCHAR(MAX)) + ',' +
' #definition=#def'
AS ExportQuery
FROM
[dbo].[sysdiagrams]
WHERE
[name] = '' -- Diagram Name
Next, you run the generated string in other DB.
As PROCEDURE:
-- =============================================
-- Author: Eduardo Cuomo
-- Description: Export Database Diagrama to SQL Query
-- =============================================
CREATE PROCEDURE [dbo].[Sys_ExportDatabaseDiagram]
#name SYSNAME -- Diagram Name
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
'DECLARE #def AS VARBINARY(MAX) ; ' +
'SELECT #def = CONVERT(VARBINARY(MAX), 0x' + CONVERT(NVARCHAR(MAX), [definition], 2) + ', 2) ; ' +
' EXEC dbo.sp_creatediagram' +
' #diagramname=''''' + [name] + ''''',' +
' #version=' + CAST([version] AS NVARCHAR(MAX)) + ',' +
' #definition=#def'
AS ExportQuery
FROM
[dbo].[sysdiagrams]
WHERE
[name] = #name
You can get rid of the UPDATE statement by fixing your INSERT statement - specifically the select portion. You are inserting the diagram_id column into the principal_id column (diagram_id is an identity).
Change it to:
DECLARE #SourceDiagramId int = 1
INSERT INTO [DestinationDB].[dbo].sysdiagrams
SELECT [name],principal_id,version,definition from [SourceDB].[dbo].sysdiagrams
WHERE diagram_id = #SourceDiagramId
And presto, it's all in there right the first time.
As in C Isaze answer, there are three simple steps:
1- Create the same number of "dummy" diagrams in the target server where you want to copy the diagrams
2- Add the target server as a Linked Server in the source server
3- run this script on source server
update [LINKEDSERVER].TARGETDB.[dbo].sysdiagrams set [definition]=
(SELECT [definition] from SOURCEDB.[dbo].sysdiagrams WHERE diagram_id = 1)
where diagram_id=1
If the databases are in different servers, there may be permission issues.
To copy the sysdiagrams, create the same number of "dummy" diagrams in the target server where you want to copy the diagrams, add the target server as a Linked Server in the source server and then run the script:
SELECT * from [LINKEDSERVER].TARGETDB.[dbo].sysdiagrams
SELECT * from SOURCEDB.[dbo].sysdiagrams
update [LINKEDSERVER].TARGETDB.[dbo].sysdiagrams set definition=
(SELECT definition from SOURCEDB.[dbo].sysdiagrams WHERE diagram_id = 1)
where diagram_id=1
-- the first 2 select commands will confirm that you are able to connect to both databases
-- then change the id as required to copy all the diagrams
There's a tool for exporting the diagrams to file and back into a database that you can find here: https://github.com/timabell/database-diagram-scm/
You'd be able to use this by pointing it at your original database and doing an export, and then pointing at your target database and doing an import.

Resources