SQL Server : how to create tables in stored procedures? - sql-server

Task: write a SQL script to build a table. Call the script spCreateTable(), it should accept one argument, a varchar called subjectOfTable.
subjectOfTable is a varchar containing the subject of the table. Some examples are example “Employee” or “Formula.” Your script will use that subject to build the table name, primary key, and natural key.
For example, given “Employee” as the subject, your script will build a table called tEmployee with a surrogate key called EmployeeID and a natural key called Employee. Be sure to create the necessary constraints for the keys.
CREATE PROCEDURE [dbo].[sp_CreateTable]
#subjectOfTable VARCHAR(30)
AS
BEGIN
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID (#tabName) AND type in (N'U'))
DROP TABLE [dbo].[#tabName]
CREATE TABLE #tabName
(
[ID] [INT] IDENTITY(1,1) NOT NULL,
[RankID] [INT] NOT NULL,
[SlotTime] [NVARCHAR](10) NOT NULL,
[SlotDate] [NVARCHAR](30) NOT NULL
) ON [PRIMARY]
END
This is what I have so far, any help would be great. Thanks!

You can create the table with a generic name, and then use the sp_rename stored procedure to rename it to the variable name:
CREATE TABLE IF EXISTS sp_CreateTable_TempTable (
[ID] [int] IDENTITY(1,1) NOT NULL,
[RankID] [int] NOT NULL,
[SlotTime] [nvarchar](10) NOT NULL,
[SlotDate] [nvarchar](30) NOT NULL
) ON [PRIMARY]
GO
EXEC sp_rename sp_CreateTable_TempTable, #subjectOfTable;
GO

Since I cannot comment, I'll just throw it out here
What's the real usage of this code anyway? to create table often but same with columns?
I don't know how other person will run this (in SSMS? in application?)
FYI SSMS also has Template for create table (Ctrl+Alt+T), then he/she can press Ctrl+Shift+M to fill in the parameters every time.
If the columns are fixed, you can customize first and send it to them so they only change the part
-- =========================================
-- Create table template
-- =========================================
USE <database, sysname, AdventureWorks>
GO
IF OBJECT_ID('<schema_name, sysname, dbo>.<table_name, sysname, sample_table>', 'U') IS NOT NULL
DROP TABLE <schema_name, sysname, dbo>.<table_name, sysname, sample_table>
GO
CREATE TABLE <schema_name, sysname, dbo>.<table_name, sysname, sample_table>
(
<columns_in_primary_key, , c1> <column1_datatype, , int> <column1_nullability,, NOT NULL>,
<column2_name, sysname, c2> <column2_datatype, , char(10)> <column2_nullability,, NULL>,
<column3_name, sysname, c3> <column3_datatype, , datetime> <column3_nullability,, NULL>,
CONSTRAINT <contraint_name, sysname, PK_sample_table> PRIMARY KEY (<columns_in_primary_key, , c1>)
)
GO

first: prefixing a stored procedure should be usually avoided
you can use dynamic sql like that: (but better read that before)
BEGIN TRAN;
GO
CREATE PROCEDURE [dbo].[CreateTable]
( #Tabname SYSNAME
, #SchemaName NVARCHAR(128) = NULL
) AS
BEGIN;
SET #SchemaName = ISNULL (#SchemaName, N'dbo');
IF ( SELECT object_id (#SchemaName + N'.'+#tabname ) ) IS NOT NULL
EXEC ( N'DROP TABLE ' + #SchemaName + N'.'+#tabname)
EXEC (N'CREATE TABLE ' + #SchemaName + N'.'+#tabname + N'
( [ID] [INT] IDENTITY(1,1) NOT NULL,
[RankID] [INT] NOT NULL,
[SlotTime] TIME NOT NULL,
[SlotDate] DATE NOT NULL
) ON [PRIMARY]'
);
RETURN 0;
END;
GO
SELECT OBJECT_ID (N'dbo.test') AS OBJECT_ID
EXEC [dbo].[CreateTable] #tabname = test
SELECT OBJECT_ID (N'dbo.test') AS OBJECT_ID
ROLLBACK

Related

IF EXISTS logic to create new table and/or columns

My goal is to make two if statements in SQL that check if a table and a column exist.
My first if statement is returning an error that says:
the table exists with the name.
In my second statement I don't know how to check if a column is already there/not there.
First SQL statement:
USE [Elearn2]
GO
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[GDPR_SupportRequest]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[GDPR_SupportRequest]
(
[Id] [uniqueidentifier] NOT NULL,
[RequestDate] [datetime2](7) NOT NULL,
[RequestType] [nvarchar](max) NOT NULL,
[RequestQuery] [nvarchar](max) NOT NULL,
[UserId] [uniqueidentifier] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER TABLE [dbo].[GDPR_SupportRequest] WITH CHECK
ADD FOREIGN KEY([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id])
END
Second SQL statement:
IF EXISTS (SELECT * FROM UserMetaData WHERE Consent != Null)
BEGIN
ALTER TABLE UserMetaData
ADD Consent BIT NULL DEFAULT 0;
END
If someone could help me to fix the statement please let me know how.
Thank you
You are checking whether the table GDPR_SupportRequest exists and creating the table if it exists, instead you should create it if the table GDPR_SupportRequest doesn't exists already.
USE [Elearn2]
GO
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[GDPR_SupportRequest]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[GDPR_SupportRequest]
(
[Id] [uniqueidentifier] NOT NULL,
[RequestDate] [datetime2](7) NOT NULL,
[RequestType] [nvarchar](max) NOT NULL,
[RequestQuery] [nvarchar](max) NOT NULL,
[UserId] [uniqueidentifier] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER TABLE [dbo].[GDPR_SupportRequest] WITH CHECK
ADD FOREIGN KEY([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id])
END
You can use the below script to check if the column Consent exists in the table UserMetaData and if the column doesn't then you can add it.
IF NOT EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'UserMetaData'
AND COLUMN_NAME = 'Consent')
BEGIN
ALTER TABLE UserMetaData
ADD Consent BIT NULL DEFAULT 0;
END

sql error in visual studio

why I am getting the following error?
Error 11 SQL71006: Only one statement is allowed per batch. A batch
separator, such as 'GO', might be required between
statements. dbo.Table_1 1 1
Here is my code:
IF OBJECT_ID('Database1') IS NOT NULL
DROP TABLE Table_1;
CREATE TABLE [dbo].[Table]
(
[departament] VARCHAR(MAX) NOT NULL PRIMARY KEY,
[specializare] VARCHAR(MAX) NOT NULL,
[student] VARCHAR(MAX) NOT NULL,
[profesor] VARCHAR(MAX) NOT NULL
)
You are missing a GO between the Statements.
IF OBJECT_ID('Database1') IS NOT NULL
DROP TABLE Table_1;
GO
CREATE TABLE [dbo].[Table]
(
[departament] VARCHAR(MAX) NOT NULL PRIMARY KEY,
[specializare] VARCHAR(MAX) NOT NULL,
[student] VARCHAR(MAX) NOT NULL,
[profesor] VARCHAR(MAX) NOT NULL
)

sql server stored procedure check if exists table in other database and rename it

Have 2 databases: MAIN and IP2LOCATION
in MAIN, I have the following stored procedure:
CREATE PROCEDURE dbo.Update_IP2Location_DB11_from_CSV
AS
BEGIN
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[ip2location].[dbo].[db11_new]') AND type in (N'U'))
BEGIN
CREATE TABLE [ip2location].[dbo].[db11_new]
(
[ip_from] bigint NOT NULL,
[ip_to] bigint NOT NULL,
[country_code] nvarchar(2) NOT NULL,
[country_name] nvarchar(64) NOT NULL,
[region_name] nvarchar(128) NOT NULL,
[city_name] nvarchar(128) NOT NULL,
[latitude] float NOT NULL,
[longitude] float NOT NULL,
[zip_code] nvarchar(30) NOT NULL,
[time_zone] nvarchar(8) NOT NULL,
) ON [PRIMARY]
CREATE INDEX [ip_from] ON [ip2location].[dbo].[db11_new]([ip_from])
END
ELSE
BEGIN
DELETE FROM [ip2location].[dbo].[db11_new]
END
BULK INSERT [ip2location].[dbo].[db11_new]
FROM 'D:\IP2LOCATION-LITE-DB11.CSV'
WITH
( FORMATFILE = 'C:\inetpub\wwwroot\ws\DB11_ip4.FMT')
EXEC sp_rename N'dbo.db11', N'db11_old', 'OBJECT'
EXEC sp_rename N'ip2location.dbo.db11_new', N'db11', 'OBJECT'
END
that does not work properly:
if db11_new does not exists, it (correctly) creates it, but if it exists.. I get
There is already an object named 'db11_new' in the database.
therefore it seems there is something wrong in
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[ip2location].[dbo].[db11_new]') AND type in (N'U'))
and also at the end of procedure with the 2 Rename I get (always) the following answer
Msg 15248, Level 11, State 1, Procedure sp_rename, Line 359
Either the parameter #objname is ambiguous or the claimed #objtype (OBJECT) is wrong.
it seems problem is because the sproc is not stored into ip2location DB but in another database..
can suggest a solution, considering that I would prefer to keep all sprocs in MAIN DB, since have all other there?
Thanks
therefore it seems there is something wrong in
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[ip2location].[dbo].[db11_new]') AND type in (N'U'))
Your analysis is correct. The sys.objects catalog view will return objects in the current database context (MAIN). Although you could just use a 3-part name (ip2location.sys.objects), I suggest you simply check for a NULL OBJECT_ID function result:
IF OBJECT_ID(N'[ip2location].[dbo].[db11_new]', 'U') IS NULL
BEGIN
CREATE TABLE [ip2location].[dbo].[db11_new]
(
[ip_from] bigint NOT NULL,
[ip_to] bigint NOT NULL,
[country_code] nvarchar(2) NOT NULL,
[country_name] nvarchar(64) NOT NULL,
[region_name] nvarchar(128) NOT NULL,
[city_name] nvarchar(128) NOT NULL,
[latitude] float NOT NULL,
[longitude] float NOT NULL,
[zip_code] nvarchar(30) NOT NULL,
[time_zone] nvarchar(8) NOT NULL,
) ON [PRIMARY];
CREATE INDEX [ip_from] ON [ip2location].[dbo].[db11_new]([ip_from]);
END;
ELSE
BEGIN
DELETE FROM [ip2location].[dbo].[db11_new];
END;
sys.objects and sp_rename are local objects.
Try to use this:
IF NOT EXISTS (SELECT * FROM ip2location.sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[db11_new]') AND type in (N'U'))
and
EXEC ip2location.sp_rename N'dbo.db11_new', N'db11', 'OBJECT'
Maybe it helps...
Alternatively, when you wanna do things in another database than the current one, you can write you code in dynamic sql and then execute it directly in the other database.
https://msdn.microsoft.com/en-us/library/ms188001.aspx
I have tested this query (without csv upload)
At first I remove every reference to ip2location:
CREATE PROCEDURE dbo.Update_IP2Location_DB11_from_CSV
AS
BEGIN
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'dbo.db11_new') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[db11_new]
(
[ip_from] bigint NOT NULL,
[ip_to] bigint NOT NULL,
[country_code] nvarchar(2) NOT NULL,
[country_name] nvarchar(64) NOT NULL,
[region_name] nvarchar(128) NOT NULL,
[city_name] nvarchar(128) NOT NULL,
[latitude] float NOT NULL,
[longitude] float NOT NULL,
[zip_code] nvarchar(30) NOT NULL,
[time_zone] nvarchar(8) NOT NULL,
) ON [PRIMARY]
CREATE INDEX [ip_from] ON [dbo].[db11_new]([ip_from])
END
ELSE
BEGIN
DELETE FROM [dbo].[db11_new]
END
BULK INSERT [dbo].[db11_new]
FROM 'D:\IP2LOCATION-LITE-DB11.CSV'
WITH
( FORMATFILE = 'C:\inetpub\wwwroot\ws\DB11_ip4.FMT')
EXEC sp_rename N'dbo.db11', N'db11_old', 'OBJECT'
EXEC sp_rename N'dbo.db11_new', N'db11', 'OBJECT'
END
GO
First run:
I have no db11* tables. Execution brings me:
Msg 15248, Level 11, State 1, Procedure sp_rename, Line 401 [Batch
Start Line 2] Either the parameter #objname is ambiguous or the
claimed #objtype (OBJECT) is wrong. Caution: Changing any part of an
object name could break scripts and stored procedures.
That means that db11_new was created, and than renamed in db11, but db11_old wasn't found, so I got this error. I get db11 table in my DB.
Second run:
Caution: Changing any part of an object name could break scripts and
stored procedures. Caution: Changing any part of an object name could
break scripts and stored procedures.
That means all was created and renamed.
Third run:
Msg 15335, Level 11, State 1, Procedure sp_rename, Line 509 [Batch
Start Line 2] Error: The new name 'db11_old' is already in use as a
OBJECT name and would cause a duplicate that is not permitted. Msg
15335, Level 11, State 1, Procedure sp_rename, Line 509 [Batch Start
Line 2] Error: The new name 'db11' is already in use as a OBJECT name
and would cause a duplicate that is not permitted.
So every next re-run You will get this same errors.
My suggestion is to do something about db11_old.
Thanks to Reboon and Dan Guzman here the solution, little improved:
CREATE PROCEDURE dbo.spA_Update_IP2Location_DB11_from_CSV
AS
BEGIN
IF OBJECT_ID(N'[ip2location].[dbo].[db11_new]', 'U') IS NULL
BEGIN
CREATE TABLE [ip2location].[dbo].[db11_new](
[ip_from] bigint NOT NULL,
[ip_to] bigint NOT NULL,
[country_code] nvarchar(2) NOT NULL,
[country_name] nvarchar(64) NOT NULL,
[region_name] nvarchar(128) NOT NULL,
[city_name] nvarchar(128) NOT NULL,
[latitude] float NOT NULL,
[longitude] float NOT NULL,
[zip_code] nvarchar(30) NOT NULL,
[time_zone] nvarchar(8) NOT NULL,
) ON [PRIMARY]
CREATE INDEX [ip_from] ON [ip2location].[dbo].[db11_new]([ip_from]) ON [PRIMARY]
END
ELSE
BEGIN
delete from [ip2location].[dbo].[db11_new]
END
BULK INSERT [ip2location].[dbo].[db11_new]
FROM 'D:\IP2LOCATION-LITE-DB11.CSV'
WITH
( FORMATFILE = 'C:\inetpub\wwwroot\ws\DB11_ip4.FMT' )
BEGIN TRANSACTION
EXEC ip2location.dbo.sp_rename N'dbo.db11', N'db11_old'
EXEC ip2location.dbo.sp_rename N'dbo.db11_new', N'db11'
IF OBJECT_ID(N'[ip2location].[dbo].[db11_old]', 'U') IS NOT NULL
BEGIN
DROP TABLE ip2location.dbo.db11_old
END
COMMIT TRANSACTION
END

SQL Server stored procedure optimal query method?

I have 5 meta tables that have the same format but depend on 5 other tables. Each Meta table looks like this:
CREATE TABLE [dbo].[SiteMetas]
(
[SiteMetaId] [bigint] IDENTITY(1,1) NOT NULL,
[SiteId] [bigint] NOT NULL,
FOREIGN KEY([SiteId]) REFERENCES [dbo].[Sites] ([SiteId]),
[MetaGroup] [nvarchar] (64) NOT NULL,
[MetaName] [nvarchar] (128) NOT NULL,
[MetaType] [char] NOT NULL DEFAULT 0, -- t, i, r, d, s, b
[MetaBool] [bit] DEFAULT NULL, -- t
[MetaInteger] [bigint] DEFAULT NULL, -- i
[MetaReal] [real] DEFAULT NULL, -- r
[MetaDateTime] [datetime] DEFAULT NULL, -- d
[MetaString] [nvarchar] (MAX) DEFAULT NULL, -- s
[MetaBinary] [varbinary] (MAX) DEFAULT NULL, -- b
[MetaCreated] [datetime] NOT NULL DEFAULT (GETUTCDATE()),
[MetaExpires] [datetime] DEFAULT NULL,
[MetaUpdated] [datetime] DEFAULT NULL,
PRIMARY KEY CLUSTERED ([SiteMetaId] ASC) WITH (IGNORE_DUP_KEY = ON),
UNIQUE NONCLUSTERED ([SiteId] ASC, [MetaGroup] ASC, [MetaName] ASC) WITH (IGNORE_DUP_KEY = ON)
);
This is for Site but there's 4 more. Like Users, ...
And I want to read the Binary meta value from Site. So wrote this stored procedure:
CREATE PROCEDURE [dbo].[GetSiteMetaBinary]
#SiteId AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128)
AS
BEGIN
SELECT TOP 1 [MetaBinary]
FROM [dbo].[SiteMetas]
WHERE [SiteId] = #SiteId
AND [MetaGroup] = #Group
AND [MetaName] = #Name
AND [MetaType] = 'b';
END;
This stored procedure has duplicates for User too... and the rest of the tables. That just replaces Site with User in its body.
But thinking that I have too many of these I wrote this one:
CREATE PROCEDURE [dbo].[GetMeta]
#Set AS nvarchar(64),
#Id AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128),
#Type AS nvarchar(16)
AS
BEGIN
DECLARE #Flag nchar(1);
DECLARE #Sql nvarchar(MAX);
SET #Flag = CASE #Type
WHEN 'Bool' THEN 't'
WHEN 'Integer' THEN 'i'
WHEN 'Real' THEN 'r'
WHEN 'DateTime' THEN 'd'
WHEN 'String' THEN 's'
WHEN 'Binary' THEN 'b'
ELSE NULL
END;
SET #Sql = N'SELECT TOP 1 [Meta' + #Type + N'] FROM [dbo].[' + #Set + N'Metas]' +
N'WHERE [' + #Set + N'Id] = #Id AND [MetaGroup] = #Group AND [MetaName] = #Name AND [MetaType] = #Flag;';
-- SELECT #Sql; -- DEBUG
EXEC sp_executesql #Sql,
N' #Id AS bigint, #Group AS nvarchar(64), #Name AS nvarchar(128), #Flag AS nchar(1)',
#Id, #Group, #Name, #Flag
;
END;
which is a general use stored procedure to read any data typed stored in a column based on input arguments. I use it like this [dbo].[GetMeta] 'Site', 1, 'group', 'name', 'Binary' the difference being that the actual query is dynamically generated so it's not known before hand by SQL Server like the first specialized variant.
Which of the two choices is better from a performance point of view and friendlier to SQL Server's internals? A dedicated one for each table and column data type of a general one that internally builds a query based on fed arguments.
I can use either. I like the last as it does not pollute my stored procedure space. :) The first one is more clear and SQL Server might be able to optimize it better. Not sure...
PS: I'm quite new to SQL Server
Static procedures are usually faster because the SQL engine can cache the compiled SP's execution plan.
However, unless this SP will be called a lot or is time-critical, it probably isn't worth worrying about it because the time savings of only having to maintain one SP will make up for the very small amount of time difference spent waiting for the SP to finish.

How to use the variables of a table being passed to another stored procedure?

I want to use my table variable "#Customers"'s variables like "#Customers.Name", I have tried it like : Customers.Name or #Customers.Name but both showing error please suggest how can i use the variables?
CREATE TYPE [dbo].[TableType] AS TABLE(
[CustomerID] [int] NOT NULL,
[Name] [varchar](255) NULL,
[City] [varchar](50) NULL,
[State] [char](2) NULL,
[Zipcode] [varchar](20) NULL
)
GO
ALTER PROCEDURE sp_GetCustomers
AS
BEGIN
DECLARE #Customers AS TableType
INSERT INTO #Customers(CustomerID,Name,City,State,ZipCode)
SELECT C.CustomerID,C.FirstName + ' ' + C.LastName,A.City,A.State,A.ZipCode
FROM Customers C INNER JOIN CustomerAddress A ON C.CustomerID=A.CustomerID
EXEC sp_GetCustomersWithAddresses #Customers
END
go
CREATE PROCEDURE sp_GetCustomersWithAddresses
#Customers TableType READONLY
AS
BEGIN
SELECT * FROM #Customers where #Customers.Name="XYZ";
END
You should use an alias in your query. Your stored procedure should look something like this:
CREATE PROCEDURE sp_GetCustomersWithAddresses
#Customers TableType READONLY
AS
BEGIN
SELECT * FROM #Customers a where a.Name='XYZ';
END
In T-SQL, you don't access table column names that way, like you might in a language like C# using classes.
But in your example, I think it's as simple as:
Select * From #Customers Where [Name] = 'XYZ'
I have 'Name' in brackets because it's a reserved word in T-SQL. And T-SQL should know that [Name] is a column because it knows that #Customers is of type TableType. So this should work.

Resources