T-SQL equivalent of C++ #define - sql-server

I have an SQL-Script for changing the source tables of multiple views at once. To do so I would like to have something like
#define source theSourceTable
instead of finding and replacing the parts I need. My understanding is that this isn't possible with local variables but is there another way?

Like I mentioned in the comments, a SYNONYM, seems to the right idea here. As we have very little to work with, here is an overly simplified example:
USE Sandbox;
GO
--Create some sample tables
CREATE TABLE dbo.MyTable1 (MyColumn int);
INSERT INTO dbo.MyTable1 (MyColumn)
VALUES (1),(2);
CREATE TABLE dbo.MyTable2 (MyColumn int);
INSERT INTO dbo.MyTable2 (MyColumn)
VALUES (3),(4);
GO
--Create synonyms
CREATE SYNONYM dbo.MyTableA FOR dbo.MyTable1;
CREATE SYNONYM dbo.MyTableB FOR dbo.MyTable1;
GO
--Create views
CREATE VIEW MyView1 AS
SELECT MyColumn, 'View1' AS ObjectName
FROM dbo.MyTableA;
GO
CREATE VIEW MyView2 AS
SELECT MyColumn, 'View2' AS ObjectName
FROM dbo.MyTableB;
GO
CREATE VIEW MyView3 AS
SELECT A.MyColumn AS AColumn,
B.MyColumn AS BColumn,
'View3' AS ObjectName
FROM dbo.MyTableA A
CROSS JOIN dbo.MyTableB B;
GO
--Check data
SELECT *
FROM dbo.MyView1;
SELECT *
FROM dbo.MyView2;
SELECT *
FROM dbo.MyView3;
GO
--Create a new table
CREATE TABLE dbo.MyTable3 (MyColumn int);
INSERT INTO dbo.MyTable3 (MyColumn)
VALUES (5),(6);
GO
--Alter the synonyms
DROP SYNONYM dbo.MyTableA;
CREATE SYNONYM dbo.MyTableA FOR dbo.MyTable2;
DROP SYNONYM dbo.MyTableB;
CREATE SYNONYM dbo.MyTableB FOR dbo.MyTable3;
GO
--Check views again:
--Check data
SELECT *
FROM dbo.MyView1;
SELECT *
FROM dbo.MyView2;
SELECT *
FROM dbo.MyView3;
GO
--Clean up
DROP VIEW dbo.MyView1;
DROP VIEW dbo.MyView2;
DROP VIEW dbo.MyView3;
DROP SYNONYM dbo.MyTableA;
DROP SYNONYM dbo.MyTableB;
DROP TABLE dbo.MyTable1;
DROP TABLE dbo.MyTable2;
DROP TABLE dbo.MyTable3;
You will need to edit the existing DDL of all your views once, to use the new synonym's name instead, but after that you can simply recreate the synonyms and they will all be updated.

You might be able to use SQLCMD mode (activate with ALT-Q-M or toggle it in the Query menu in SSMS).
:SETVAR MyTable SomeTableName
CREATE OR ALTER VIEW dbo.myView
AS
SELECT * FROM dbo.$(MyTable)
GO
This will end up with a view that selects * from SomeTableName
Alternatively you can possibly use dynamic sql
DECLARE #sql VARCHAR(MAX)
DECLARE #myTable VARCHAR(100) = 'SomeTableName'
SET #sql = 'CREATE OR ALTER VIEW dbo.myView
AS
SELECT * FROM dbo.' + #myTable
EXEC(#sql)
which has the same end result.
(CREATE OR ALTER is SQL2016 syntactic sugar, if you are on an older version adjust accordingly)

Related

SQL Server: same table, multiple names

I have some SQL Server database tables that need to be renamed.
Unfortunately, those tables are used by some libraries for which I have no source code.
I plan to rewrite those libraries, but it will take some time.
In the meantime I wonder if there's a way to create an "alias" for my tables, so that they can be referenced with two different names.
I could create views like SELECT * FROM OldName but I'm concerned about performance.
Create a SYNONYM for the old name, after you rename the table. In short:
--Create example table
CREATE TABLE dbo.YourTable (ID int);
GO
--Rename it
EXEC sys.sp_rename N'dbo.YourTable',N'MyTable';
GO
--Create a synonym for the new name, with the old name
CREATE SYNONYM dbo.YourTable FOR dbo.MyTable;
GO
--Try selecting from old name, it works!
SELECT *
FROM YourTable;
GO
--Clean up
DROP SYNONYM dbo.YourTable;
GO
DROP TABLE dbo.MyTable;
A view should cause no performance problem, as the query is converted by SQL. But synonyms are a nice option as well.

Stored Proc scope with Create table/view/index & Select in single SP

I have a sql script creating views/tables/indexes and some selects all in the one script with GO statement after each table/view creation and i get errors.. It is possible hash them all in one script???
IF OBJECT_ID('dbo.ABC', 'U') IS NOT NULL
DROP TABLE dbo.ABC;
GO
select v.*
INTO dbo.ABC
from dbo.BC v
GO
CREATE NONCLUSTERED INDEX IX_ABC_ID ON dbo.ABC ([ID])
GO
GO
CREATE VIEW [dbo].[vABC] AS
SELECT [ID]
,[Description]
,[Name]
FROM [dbo].[NewDRGTable]
GO
You can use EXEC statement for each part like this (don't forget to escape apostrophe):
EXEC('IF OBJECT_ID(''dbo.ABC'', ''U'') IS NOT NULL
DROP TABLE dbo.ABC;')
etc.

Truncate Table With Foreign Key

I have one database having above 1000+ tables. I am creating fresh Blank setup. For that i have to truncate all the tables but some of the tables i do not wish to truncate so i created on table and stored the names in the table.
----------- Create hardcode table ----------------------
Create table TblHardCodeTableNotToTruncate(TableName varchar(100))
go
insert into TblHardCodeTableNotToTruncate
select 'TblHardCodeTableNotToTruncate'
go
---- insert the table names which dont wish to truncate ------------
Insert into TblHardCodeTableNotToTruncate
select 'TblAccount'
go
Insert into TblHardCodeTableNotToTruncate
select 'TblCity'
go
etc, Following is the query i used to truncate all the tables except these inserted tables
------------------------- Truncate all the tables except the tables specified in the Hardcode table -------------------------
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
GO
EXEC sp_MSForEachTable 'ALTER TABLE ? DISABLE TRIGGER ALL'
GO
DECLARE #TBLTEMP TABLE(TABLENAME VARCHAR(100))
insert into #TBLTEMP
select name from sysobjects where xtype = 'U'
delete from #TBLTEMP where tablename in (
select tablename from TblHardCodeTableNotToTruncate)
DECLARE #SQLQUERY VARCHAR(MAX) =''
DECLARE #INTCNT INT = 1
DECLARE #TABLENAME VARCHAR(100) =''
WHILE (SELECT COUNT(*) FROM #TBLTEMP) > 0
BEGIN
select top 1 #TABLENAME = TABLENAME from #TBLTEMP
SET #SQLQUERY = 'Truncate table ' + #TABLENAME
EXEC(#SQLQUERY)
PRINT #SQLQUERY
DELETE FROM #TBLTEMP WHERE TABLENAME = #TABLENAME
END
go
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'
GO
EXEC sp_MSForEachTable 'ALTER TABLE ? ENABLE TRIGGER ALL'
go
But i get the error foreign key reference error. I know i need to drop constraint before doing it. But is there any way to do it as i cannot drop then truncate and again add each time with so many tables. Is there any unique script can we write in below which will drop constraint if exists,truncate table and add the dropped constraint again to the table again.
I'm afraid that there is no way to do that without dropping constraints,
you can write a code that extracts the constraints for the tables and drop them it's simple.
Create a temporary table and use this code to get the tables constraint and notice that the sysconstraints.status must be 1 or 3 to get foreign and primary keys:
Select SysObjects.[Name] As "Constraint Name",
Tabls.[Name] as "Table Name",
Cols.[Name] As "Column Name"
From SysObjects Inner Join
(Select [Name],[ID] From SysObjects) As Tabls
On Tabls.[ID] = Sysobjects.[Parent_Obj]
Inner Join sysconstraints On sysconstraints.Constid = Sysobjects.[ID]
Inner Join SysColumns Cols On Cols.[ColID] = sysconstraints.[ColID]
And Cols.[ID] = Tabls.[ID]
where sysconstraints.status in (1, 3)
order by [Tabls].[Name]
you gonna have to recreate these constraints again after truncate, that's simple too, but I'm not gonna do all your job :)
Even if all tabled are empty and you disable all constraints the truncate ill fail due to the mere FK existence. You really must drop it.
You can create a script to automate it (truncate all tables dropping and recreating FKs)
I found one here (use with discretion)
http://www.sqlservercentral.com/scripts/Development/62892/
Also if you just want to create a "blanket" DB you can export the script to create all objects and just populate that few (domain?) tables do you need.

How to drop a table if it exists?

The table name is Scores.
Is it correct to do the following?
IF EXISTS(SELECT *
FROM dbo.Scores)
DROP TABLE dbo.Scores
Is it correct to do the following?
IF EXISTS(SELECT *
FROM dbo.Scores)
DROP TABLE dbo.Scores
No. That will drop the table only if it contains any rows (and will raise an error if the table does not exist).
Instead, for a permanent table you can use
IF OBJECT_ID('dbo.Scores', 'U') IS NOT NULL
DROP TABLE dbo.Scores;
Or, for a temporary table you can use
IF OBJECT_ID('tempdb.dbo.#TempTableName', 'U') IS NOT NULL
DROP TABLE #TempTableName;
SQL Server 2016+ has a better way, using DROP TABLE IF EXISTS …. See the answer by #Jovan.
From SQL Server 2016 you can use
DROP TABLE IF EXISTS dbo.Scores
Reference: DROP IF EXISTS - new thing in SQL Server 2016
It will be in SQL Azure Database soon.
The ANSI SQL/cross-platform way is to use the INFORMATION_SCHEMA, which was specifically designed to query meta data about objects within SQL databases.
if exists (select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'Scores' AND TABLE_SCHEMA = 'dbo')
drop table dbo.Scores;
Most modern RDBMS servers provide, at least, basic INFORMATION_SCHEMA support, including: MySQL, Postgres, Oracle, IBM DB2, and Microsoft SQL Server 7.0 (and greater).
Have seen so many that don't really work.
when a temp table is created it must be deleted from the tempdb!
The only code that works is:
IF OBJECT_ID('tempdb..#tempdbname') IS NOT NULL --Remove dbo here
DROP TABLE #tempdbname -- Remoeve "tempdb.dbo"
In SQL Server 2016 (13.x) and above
DROP TABLE IF EXISTS dbo.Scores
In earlier versions
IF OBJECT_ID('dbo.Scores', 'U') IS NOT NULL
DROP TABLE dbo.Scores;
U is your table type
Or:
if exists (select * from sys.objects where name = 'Scores' and type = 'u')
drop table Scores
I hope this helps:
begin try drop table #tempTable end try
begin catch end catch
I wrote a little UDF that returns 1 if its argument is the name of an extant table, 0 otherwise:
CREATE FUNCTION [dbo].[Table_exists]
(
#TableName VARCHAR(200)
)
RETURNS BIT
AS
BEGIN
If Exists(select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = #TableName)
RETURN 1;
RETURN 0;
END
GO
To delete table User if it exists, call it like so:
IF [dbo].[Table_exists]('User') = 1 Drop table [User]
Simple is that:
IF OBJECT_ID(dbo.TableName, 'U') IS NOT NULL
DROP TABLE dbo.TableName
where dbo.TableName is your desired table and 'U' is type of your table.
IF EXISTS (SELECT NAME FROM SYS.OBJECTS WHERE object_id = OBJECT_ID(N'Scores') AND TYPE in (N'U'))
DROP TABLE Scores
GO
SQL Server 2016 and above the best and simple one is
DROP TABLE IF EXISTS [TABLE NAME]
Ex:
DROP TABLE IF EXISTS dbo.Scores
if suppose the above one is not working then you can use the below one
IF OBJECT_ID('dbo.Scores', 'u') IS NOT NULL
DROP TABLE dbo.Scores;
I use:
if exists (select *
from sys.tables
where name = 'tableName'
and schema_id = schema_id('dbo'))
begin
drop table dbo.tableName
end
Make sure to use cascade constraint at the end to automatically drop all objects that depend on the table (such as views and projections).
drop table if exists tableName cascade;
If you use long codes and want to write less for temporary table create this procedure:
CREATE PROCEDURE MF_DROP (#TEMP AS VARCHAR(100)) AS
EXEC('IF OBJECT_ID(''TEMPDB.DBO.' + #TEMP + ''', ''U'') IS NOT NULL DROP TABLE ' + #TEMP)
In execution:
EXEC MF_DROP #A
CREATE TABLE #A (I INT) ....
A better visual and easy way, if you are using Visual Studio, just open from menu bar,
View -> SQL Server Object Explorer
it should open like shown here
Select and Right Click the Table you wish to delete, then delete. Such a screen should be displayed. Click Update Database to confirm.
This method is very safe as it gives you the feedback and will warn of any relations of the deleted table with other tables.

Add a new table column to specific ordinal position in Microsoft SQL Server

Is it possible to add a column to a table at a specific ordinal position in Microsoft SQL Server?
For instance, our tables always have CreatedOn, CreatedBy, LastModifiedOn, LastModifiedBy columns at the "end" of each table definition? I'd like the new column to show up in SSMS above these columns.
If I am scripting all my database changes, is there a way to preserve this order at the end of the table?
FYI, I'm not trying to institute a flame war on if this should even be done. If you want to read about a thread that degenerates quickly into that, here's a good one:
http://www.developersdex.com/sql/message.asp?p=581&r=5014513
You have to create a temp table that mirrors the original table's schema but with the column order that you want, then copy the contents of the original to temp. Delete the original and rename the temp.
This is what SQL Management Studio does behind the scenes.
With a schema sync tool, you can generate these scripts automatically.
go into SQL Server management Studio, and "design" an existing table. Insert a column in the middle, right click in an empty area and select Generate Change Script...
Now look at the script it creates. it will basically create a temp table with the proper column order, insert the data from the original table, drop the original table, and rename the temp table. This is probably what you'll need to do.
You may also need to uncheck this option to allow creation of change scripts
The answer is yes, it is technically possible, but you will have a headache doing so and it will take a long time to execute and set up.
One: Create/Copy/Drop/Rename
This is actually what SQL Server is doing in the graphical interface: here's an example of the script it is generating and executing when you click the 'save' button after adding a new column to the beginning of a table.
/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_SomeTable
(
MyNewColumn int NOT NULL,
OriginalIntColumn int NULL,
OriginalVarcharColumn varchar(100) NULL
) ON [PRIMARY]
TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_SomeTable SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT dbo.Tmp_SomeTable ON
GO
IF EXISTS(SELECT * FROM dbo.SomeTable)
EXEC('INSERT INTO dbo.Tmp_SomeTable (OriginalIntColumn, OriginalVarcharColumn FROM dbo.SomeTable WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_SomeTable OFF
GO
DROP TABLE dbo.SomeTable
GO
EXECUTE sp_rename N'dbo.Tmp_SomeTable', N'SomeTable', 'OBJECT'
GO
GO
COMMIT
Two: ADD COLUMN / UPDATE / DROP COLUMN / RENAME
This method basically involves creating a copy of any existing columns that you want to add to the 'right' of your new column, transferring the data to the new column, then dropping the originals and renaming the new ones. This will play havoc with any indexes or constraints you have, since you have to repoint them. It's technically possible, but again time-consuming both in terms of development and execution.
CREATE TABLE MyTest (a int, b int, d int, e int)
INSERT INTO MyTest (a,b,d,e) VALUES(1,2,4,5)
SELECT * FROM MyTest -- your current table
ALTER TABLE MyTest ADD c int -- add a new column
ALTER TABLE MyTest ADD d_new int -- create copies of the existing columns you want to move
ALTER TABLE MyTest ADD e_new int
UPDATE MyTest SET d_new = d, e_new = e -- transfer data to the new columns
ALTER TABLE MyTest DROP COLUMN d -- remove the originals
ALTER TABLE MyTest DROP COLUMN e
EXEC SP_RENAME 'MyTest.d_new', 'd'; -- rename the new columns
EXEC SP_RENAME 'MyTest.e_new', 'e';
SELECT * FROM MyTest
DROP TABLE MyTest -- clean up the sample
Three: Live with it
This mightily offends my sense of order ... but sometimes, it just isn't worth reshuffling.
To my knowledge there is no known method to change the order of the column. Behind the scenes SQL Management Studio does what Jose Basilio said. And if you have a big table then it is impractical to change the column orders like this way.
You can use a "view". With SQL views you can use any order you like without getting affected by the table column changes.
I am using SSMS 18. I did in simple way
Opened design of table
positioning the required column by dragging it
And as per the answer from KM (second in thread) - uncheck the option to allow creation of change scripts refer image above.
Save the changes.
Done. Check your table now.
TFS 2013 will do this for you automatically.
Add the new column(s) to your table anyway you like, and then commit your changes to TFS. From there you can open the table's sql file in Visual Studio and manually move the order of the columns in the T-SQL CREATE script. Then you can update your target database by using VS's schema compare tool found under Tools > SQL Server > New Schema Comparison. Choose your Database project with your change as the source, and the database you want to update as the target. Compare, select the table's script, and Update. VS will drop and add automatically. All your data will be safe, and indexes too.
What i think is simple is to add the column ALTER TABLE table1 ADD .. and then create a tmp table like tmp_table1 from the select like
SELECT col1,col2,col5,col3,col4 into tmp_table1 from table1;
and then drop table1 and rename the tmp_table1 to table1, that is it. I hope it will help someone
Select all the columns into a temp table, and create a new table with the new column you want. Then drop the old table, select all the columns from the temp table, and insert them into the new table with the reordered column. No data is lost.
SELECT * FROM TEMP
SELECT * FROM originaltbl
SELECT * FROM #Stagintbl
DECLARE #ColumnName nvarchar(max);
SET #ColumnName=(SELECT
DISTINCT STUFF((
SELECT ',' + a.COLUMN_NAME
FROM (
SELECT Column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='originaltbl') a
for xml path('')
),1,1,'') AS ColumnName)
DECLARE #Sqlquery nvarchar(max)
SET #Sqlquery = 'SELECT ' + #ColumnName + ' FROM #Stagintbl' + '';
INSERT INTO originaltbl
EXECUTE(#Sqlquery)
Dirty and simple.
Export table to csv.
Insert new data at desired position.
Drop table.
Create new table with desired column specifications.
Load columns from csv to new table.
I am not sure if the thread is still active. I was having the same query with MySQL database. Right clicking the table and selecting 'ALTER' auto generated the below code. Sample provided from sakila db and it worked. Just find out the column after which you want to place your new column and use 'AFTER' keyword
ALTER TABLE `sakila`.`actor`
CHANGE COLUMN `middle_name` `middle_name` VARCHAR(50) NULL DEFAULT NULL AFTER `first_name`;

Resources