T-SQL How to create tables dynamically in stored procedures? - sql

Code like this, but it's wrong:
CREATE PROC sp_createATable
#name VARCHAR(10),
#properties VARCHAR(500)
AS
CREATE TABLE #name
(
id CHAR(10) PRIMARY KEY,
--...Properties extracted from #properties
);
Could you tell me how to deal with it? It really troubles me.

You are using a table variable i.e. you should declare the table. This is not a temporary table.
You create a temp table like so:
CREATE TABLE #customer
(
Name varchar(32) not null
)
You declare a table variable like so:
DECLARE #Customer TABLE
(
Name varchar(32) not null
)
Notice that a temp table is declared using # and a table variable is declared using a #.
Go read about the difference between table variables and temp tables.
UPDATE:
Based on your comment below you are actually trying to create tables in a stored procedure. For this you would need to use dynamic SQL. Basically dynamic SQL allows you to construct a SQL Statement in the form of a string and then execute it. This is the ONLY way you will be able to create a table in a stored procedure. I am going to show you how and then discuss why this is not generally a good idea.
Now for a simple example (I have not tested this code but it should give you a good indication of how to do it):
CREATE PROCEDURE sproc_BuildTable
#TableName NVARCHAR(128)
,#Column1Name NVARCHAR(32)
,#Column1DataType NVARCHAR(32)
,#Column1Nullable NVARCHAR(32)
AS
DECLARE #SQLString NVARCHAR(MAX)
SET #SQString = 'CREATE TABLE '+#TableName + '( '+#Column1Name+' '+#Column1DataType +' '+#Column1Nullable +') ON PRIMARY '
EXEC (#SQLString)
GO
This stored procedure can be executed like this:
sproc_BuildTable 'Customers','CustomerName','VARCHAR(32)','NOT NULL'
There are some major problems with this type of stored procedure.
Its going to be difficult to cater for complex tables. Imagine the following table structure:
CREATE TABLE [dbo].[Customers] (
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [nvarchar](64) NOT NULL,
[CustomerSUrname] [nvarchar](64) NOT NULL,
[CustomerDateOfBirth] [datetime] NOT NULL,
[CustomerApprovedDiscount] [decimal](3, 2) NOT NULL,
[CustomerActive] [bit] NOT NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Customers] ADD CONSTRAINT [DF_Customers_CustomerApprovedDiscount] DEFAULT ((0.00)) FOR [CustomerApprovedDiscount]
GO
This table is a little more complex than the first example, but not a lot. The stored procedure will be much, much more complex to deal with. So while this approach might work for small tables it is quickly going to be unmanageable.
Creating tables require planning. When you create tables they should be placed strategically on different filegroups. This is to ensure that you don't cause disk I/O contention. How will you address scalability if everything is created on the primary file group?
Could you clarify why you need tables to be created dynamically?
UPDATE 2:
Delayed update due to workload. I read your comment about needing to create a table for each shop and I think you should look at doing it like the example I am about to give you.
In this example I make the following assumptions:
It's an e-commerce site that has many shops
A shop can have many items (goods) to sell.
A particular item (good) can be sold at many shops
A shop will charge different prices for different items (goods)
All prices are in $ (USD)
Let say this e-commerce site sells gaming consoles (i.e. Wii, PS3, XBOX360).
Looking at my assumptions I see a classical many-to-many relationship. A shop can sell many items (goods) and items (goods) can be sold at many shops. Let's break this down into tables.
First I would need a shop table to store all the information about the shop.
A simple shop table might look like this:
CREATE TABLE [dbo].[Shop](
[ShopID] [int] IDENTITY(1,1) NOT NULL,
[ShopName] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_Shop] PRIMARY KEY CLUSTERED
(
[ShopID] ASC
) WITH (
PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
Let's insert three shops into the database to use during our example. The following code will insert three shops:
INSERT INTO Shop
SELECT 'American Games R US'
UNION
SELECT 'Europe Gaming Experience'
UNION
SELECT 'Asian Games Emporium'
If you execute a SELECT * FROM Shop you will probably see the following:
ShopID ShopName
1 American Games R US
2 Asian Games Emporium
3 Europe Gaming Experience
Right, so now let's move onto the Items (goods) table. Since the items/goods are products of various companies I am going to call the table product. You can execute the following code to create a simple Product table.
CREATE TABLE [dbo].[Product](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductDescription] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Let's populate the products table with some products. Execute the following code to insert some products:
INSERT INTO Product
SELECT 'Wii'
UNION
SELECT 'PS3'
UNION
SELECT 'XBOX360'
If you execute SELECT * FROM Product you will probably see the following:
ProductID ProductDescription
1 PS3
2 Wii
3 XBOX360
OK, at this point you have both product and shop information. So how do you bring them together? Well we know we can identify the shop by its ShopID primary key column and we know we can identify a product by its ProductID primary key column. Also, since each shop has a different price for each product we need to store the price the shop charges for the product.
So we have a table that maps the Shop to the product. We will call this table ShopProduct. A simple version of this table might look like this:
CREATE TABLE [dbo].[ShopProduct](
[ShopID] [int] NOT NULL,
[ProductID] [int] NOT NULL,
[Price] [money] NOT NULL,
CONSTRAINT [PK_ShopProduct] PRIMARY KEY CLUSTERED
(
[ShopID] ASC,
[ProductID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
So let's assume the American Games R Us shop only sells American consoles, the Europe Gaming Experience sells all consoles and the Asian Games Emporium sells only Asian consoles. We would need to map the primary keys from the shop and product tables into the ShopProduct table.
Here is how we are going to do the mapping. In my example the American Games R Us has a ShopID value of 1 (this is the primary key value) and I can see that the XBOX360 has a value of 3 and the shop has listed the XBOX360 for $159.99
By executing the following code you would complete the mapping:
INSERT INTO ShopProduct VALUES(1,3,159.99)
Now we want to add all product to the Europe Gaming Experience shop. In this example we know that the Europe Gaming Experience shop has a ShopID of 3 and since it sells all consoles we will need to insert the ProductID 1, 2 and 3 into the mapping table. Let's assume the prices for the consoles (products) at the Europe Gaming Experience shop are as follows: 1- The PS3 sells for $259.99 , 2- The Wii sells for $159.99 , 3- The XBOX360 sells for $199.99.
To get this mapping done you would need to execute the following code:
INSERT INTO ShopProduct VALUES(3,2,159.99) --This will insert the WII console into the mapping table for the Europe Gaming Experience Shop with a price of 159.99
INSERT INTO ShopProduct VALUES(3,1,259.99) --This will insert the PS3 console into the mapping table for the Europe Gaming Experience Shop with a price of 259.99
INSERT INTO ShopProduct VALUES(3,3,199.99) --This will insert the XBOX360 console into the mapping table for the Europe Gaming Experience Shop with a price of 199.99
At this point you have mapped two shops and their products into the mapping table. OK, so now how do I bring this all together to show a user browsing the website? Let's say you want to show all the product for the European Gaming Experience to a user on a web page – you would need to execute the following query:
SELECT Shop.*
, ShopProduct.*
, Product.*
FROM Shop
INNER JOIN ShopProduct ON Shop.ShopID = ShopProduct.ShopID
INNER JOIN Product ON ShopProduct.ProductID = Product.ProductID
WHERE Shop.ShopID=3
You will probably see the following results:
ShopID ShopName ShopID ProductID Price ProductID ProductDescription
3 Europe Gaming Experience 3 1 259.99 1 PS3
3 Europe Gaming Experience 3 2 159.99 2 Wii
3 Europe Gaming Experience 3 3 199.99 3 XBOX360
Now for one last example, let's assume that your website has a feature which finds the cheapest price for a console. A user asks to find the cheapest prices for XBOX360.
You can execute the following query:
SELECT Shop.*
, ShopProduct.*
, Product.*
FROM Shop
INNER JOIN ShopProduct ON Shop.ShopID = ShopProduct.ShopID
INNER JOIN Product ON ShopProduct.ProductID = Product.ProductID
WHERE Product.ProductID =3 -- You can also use Product.ProductDescription = 'XBOX360'
ORDER BY Price ASC
This query will return a list of all shops which sells the XBOX360 with the cheapest shop first and so on.
You will notice that I have not added the Asian Games shop. As an exercise, add the Asian games shop to the mapping table with the following products:
the Asian Games Emporium sells the Wii games console for $99.99 and the PS3 console for $159.99. If you work through this example you should now understand how to model a many-to-many relationship.
I hope this helps you in your travels with database design.

You will need to build that CREATE TABLE statement from the inputs and then execute it.
A simple example:
declare #cmd nvarchar(1000), #TableName nvarchar(100);
set #TableName = 'NewTable';
set #cmd = 'CREATE TABLE dbo.' + quotename(#TableName, '[') + '(newCol int not null);';
print #cmd;
--exec(#cmd);

First up, you seem to be mixing table variables and tables.
Either way, You can't pass in the table's name like that. You would have to use dynamic TSQL to do that.
If you just want to declare a table variable:
CREATE PROC sp_createATable
#name VARCHAR(10),
#properties VARCHAR(500)
AS
declare #tablename TABLE
(
id CHAR(10) PRIMARY KEY
);
The fact that you want to create a stored procedure to dynamically create tables might suggest your design is wrong.

This is a way to create tables dynamically using T-SQL stored procedures:
declare #cmd nvarchar(1000), #MyTableName nvarchar(100);
set #MyTableName = 'CustomerDetails';
set #cmd = 'CREATE TABLE dbo.' + quotename(#MyTableName, '[') + '(ColumnName1 int not null,ColumnName2 int not null);';
Execute it as:
exec(#cmd);

You can write the below code:-
create procedure spCreateTable
as
begin
create table testtb(Name varchar(20))
end
execute it as:-
exec spCreateTable

Related

Postgres Import from different table

I'm still fairly new to postgres. I have a table named: university_table with fields: name,
nationality, abbreviation, adjective, person.
I found this sql query to insert data from: https://stackoverflow.com/a/21759321/9469766
Snippet of query below.
How can alter the query to insert these values into my university_country table
-- Create and load Nationality Table - English
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Nationality]') AND type in (N'U'))
DROP TABLE [dbo].[Nationality]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-------------------------------------------------------------------
-- TABLE: [dbo].[Nationality]
-- Creation Date: 02/12/2014
-- Created by: Dan Flynn, Sr. DBA
--
-------------------------------------------------------------------
CREATE TABLE [dbo].[Nationality]
(
[NationalityID] [int] IDENTITY(1,1) NOT NULL,
[Country] [nvarchar](50) NULL,
[Abbreviation] [nvarchar](5) NULL,
[Adjective] [nvarchar] (130) NULL,
[Person] [nvarchar] (60) NULL
) ON [PRIMARY]
GO
-------------------------------------------------------------------------------
-- INSERT VALUES
-------------------------------------------------------------------------------
INSERT INTO [dbo].[Nationality](Country, Abbreviation, Adjective, Person )
VALUES ( 'AMERICAN - USA','US','US (used attributively only, as in US aggression but not He is US)','a US citizen' ),
( 'ARGENTINA','AR','Argentinian','an Argentinian' ),
( 'AUSTRALIA','AU','Australian','an Australian' ),
( 'BAHAMAS','BS','Bahamian','a Bahamian' ),
( 'BELGIUM','BE','Belgian','a Belgian' ),
GO
-------------------------------------------------------------------------------
-- ADD CLUSTERED INDEX
-------------------------------------------------------------------------------
CREATE CLUSTERED INDEX [idxNationality] ON [dbo].[Nationality]
(
[NationalityID] ASC,
[Country] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
EXEC sys.sp_addextendedproperty #name=N'TableDiscription', #value=N'CreatedBy: Dan Flynn, Sr. SQL Server DBA
CreationDate: 02/12/2014
Nationality table contains five columns, i.e.:
1. NationalityID, 2. Country, 3. Abbreviation, 4. Adjective, 5. Person
IDs 1 to 34 are alphabetical countries that are statistically the most popular as far as interaction with the United States. IDs 35 to 248 are also alphabetical for the rest of the countries.
' , #level0type=N'SCHEMA',#level0name=N'dbo', #level1type=N'TABLE',#level1name=N'Nationality'
GO
To convert T-SQL to be compatible with Postres' SQL dialect you can use the following steps.
Remove all square brackets (they are illegal in SQL identifiers). If you have identifiers that require them use double quotes " but I would highly recommend to avoid quoted identifiers completely (so never use " in SQL)
Remove all GO statements and end the statements with ; (Something that is recommended for SQL Server as well)
Remove the [dbo]. schema prefix if you didn't create one in Postgres (you typically don't)
Remove the ON [Primary] option it's not needed in Postgres (the equivalent would be to define a tablespace, but that's hardly ever needed in Postgres)
There is no IF in SQL (or Postgres), to conditionally drop a table use DROP TABLE IF EXISTS ....
There are no clustered indexes in Postgres, so just make that a regular index and remove all the options that are introduced by the WITH keyword.
Comments on tables are defined through comment on, not by calling a stored procedure
identity(x,y) needs to be replaced with the standard SQL generated always as identity
There is no nvarchar type, just make everything varchar and make sure your database was created with an encoding that can store multi-byte characters (by default it's UTF-8, so that should be fine)
Not required, but: it's highly recommended to use snake_case identifiers, rather than CamelCase in Postgres
Putting that all together the script should be something like this:
DROP TABLE IF EXISTS Nationality CASCADE;
CREATE TABLE nationality
(
Nationality_id int generated always as IDENTITY NOT NULL,
Country varchar(50) NULL,
Abbreviation varchar(5) NULL,
Adjective varchar (130) NULL,
Person varchar (60) NULL
);
INSERT INTO Nationality (Country, Abbreviation, Adjective, Person )
VALUES ( 'AMERICAN - USA','US','US (used attributively only, as in US aggression but not He is US)','a US citizen' ),
( 'ARGENTINA','AR','Argentinian','an Argentinian' ),
( 'AUSTRALIA','AU','Australian','an Australian' ),
( 'BAHAMAS','BS','Bahamian','a Bahamian' ),
( 'BELGIUM','BE','Belgian','a Belgian' );
CREATE INDEX idx_Nationality ON Nationality
(
Nationality_ID ASC,
Country ASC
);
comment on table nationality is 'CreatedBy: Dan Flynn, Sr. SQL Server DBA
CreationDate: 02/12/2014
Nationality table contains five columns, i.e.:
1. NationalityID, 2. Country, 3. Abbreviation, 4. Adjective, 5. Person
IDs 1 to 34 are alphabetical countries that are statistically the most popular as far as interaction with the United States. IDs 35 to 248 are also alphabetical for the rest of the countries.
';
I am a bit surprised that there is no primary key defined. You probably want to add:
alter table nationality
add primary key (nationality_id);
There is an SQL standard, but nobody implements it fully. Some database systems like PostgreSQL are better at sticking to the standard, others like Microsoft SQL Server are not.
The upshot of this is that you cannot take SQL that works on one RDBMS and use it with another one. You will have to translate that to the PostgreSQL dialect.

SQL Server create primary key constraint duplicate key error

I have been experiencing some strange behaviour with one of my SQL commands taken from one of our stored procedures.
This command follows the below order of execution:
1) Drop table
2) Select * into table name from live server
3) Alter table to apply PK - this step fails once out of 4 daily executions
My SQL statement:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'
[inf].[tblBase_MyTable]') AND type in (N'U'))
DROP TABLE [inf].[tblBase_MyTable]
SELECT * INTO [inf].[tblBase_MyTable]
FROM LiveServer.KMS_ALLOCATION WITH (NOLOCK)
ALTER TABLE [inf].[tblBase_MyTable] ADD
CONSTRAINT [PK_KMS_ALLOCATION] PRIMARY KEY NONCLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY =
OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GRANT SELECT ON [inf].[tblBase_MyTable] TO ourGroup
This is very strange considering the table is dropped, and I thought the indexes / keys would also be dropped. However I get this error at the same time every day. Any advice would be very much appreciated.
Error:
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'inf.tblBase_MyTable' and the index name 'PK_KMS_ALLOCATION'.
Duplicate keys in [inf].[tblBase_MyTable] table are actually possible thanks to the WITH (NOLOCK) hint which allows "dirty reads". Have a look at blog which describes this in detail: SQL Server NOLOCK Hint & other poor ideas:
What many people think NOLOCK is doing
Most people think the NOLOCK hint just reads rows & doesn’t have to
wait till others have committed their updates or selects. If someone
is updating, that is OK. If they’ve changed a value then 99.999% of
the time they will commit, so it’s OK to read it before they commit.
If they haven’t changed the record yet then it saves me waiting, its
like my transaction happened before theirs did.
The Problem
The issue is that transactions do more than just update the row. Often
they require an index to be updated OR they run out of space on the
data page. This may require new pages to be allocated & existing rows
on that page to be moved, called a PageSplit. It is possible for your
select to completely miss a number of rows &/or count other rows
twice.
Well... you might have to repeat creating the new table and filling it until the check-query from #DarkoMartinovic does not return duplicates. Only then you can continue to add the PK. But this solution might cause heavy load on your live system. And you nave no guarantee that you have a 1:1 copy of the data as well.
Having reviewed various helpful comments here, I have decided against (for now) implementing SNAPSHOT isolation as this interface does not make use of a proper staging environment.
To move to this would mean either creating a staging area and setting that database to READ COMMITTED SNAPSHOT isolation, and a rebuild of the entire interface.
To that end and on the basis of saving development time, we have opted for ensuring that any ghost reads where dupes could be brought across from the source are handled before applying the PK.
This is by no means an ideal solution in terms of performance on the target server but will provide some headroom for now and certainly remove the previous error.
SQL approach below:
DECLARE #ALLOCTABLE TABLE
(SEQ INT, ID NVARCHAR(1000), CLASSID NVARCHAR(1000), [VERSION] NVARCHAR(25), [TYPE]
NVARCHAR(100), VERSIONSEQUENCE NVARCHAR(100), VERSIONSEQUENCE_TO NVARCHAR(100),
BRANCHID NVARCHAR(100), ISDELETED INT, RESOURCE_CLASS NVARCHAR(25), RESOURCE_ID
NVARCHAR(100), WARD_ID NVARCHAR(100), ISCOMPLETE INT, TASK_ID NVARCHAR(100));
------- ALLOCATION
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[inf].
[tblBase_MyTable]') AND type in (N'U'))
DROP TABLE [inf].[tblBase_MyTable]
SELECT * INTO [inf].[tblBase_MyTable]
FROM LiveServer.KMS_ALLOCATION WITH (NOLOCK)
INSERT INTO #ALLOCTABLE
SELECT *
FROM
(SELECT
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ISCOMPLETE DESC) SEQ, AL.*
FROM [inf].[tblBase_MyTable] AL
)DUPS
WHERE SEQ >1
DELETE FROM [inf].[tblBase_MyTable]
WHERE ID IN (SELECT ID FROM #ALLOCTABLE)
AND ISCOMPLETE = 0
ALTER TABLE [inf].[tblBase_MyTable] ADD CONSTRAINT
[PK_KMS_ALLOCATION] PRIMARY KEY NONCLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GRANT SELECT ON [inf].[tblBase_MyTable] TO OurGroup

How to design a mapping table?

Im having a bit of a design issue here. Im kind of a novice in this, so I need some help.
Due to a company merge, where everything must go in one of the systems; Im supposed to map our customers with a new customerid in the other company.
When I get the new customerID's Im supposed to ensure that it is unique and the same goes for our existing customerID.
Current customerID: CurCustID
New customer ID: NewCustID
First, I would like the database to make sure that every CurCustID in column CurCustID is unique - only with one record, secondly I would like the column NewCustID to be unique - only with one record.
Third I would like that the row combination of CurCustID and NewCustID only accepts unique data.
If you can help me I would be very thankful, on the otherhand if my approach is bad practice and there is a best practice way of doing this, then please let me know.
USE [Database]
GO
/****** Object: Table [dbo].[TblMapning] Script Date: 05/30/2016 14:30:21 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[TblMapning](
[CurCustID] [varchar](255) NOT NULL,
[NewCustID] [varchar](255) NOT NULL,
PRIMARY KEY CLUSTERED
(
[CurCustID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Seems like you'd have to create 3 separate tables to enforce all those things. How new customer ids are generated in the new and mapping tables could be automated depending on your house rules for how new ones are assigned. You would probably want to create code or SPs to make the process more user-friendly, spit out errors, etc. for duped oldcustids, but at the table level this would be one way to enforce it.
CREATE TABLE [dbo].[Tbloldcustids](
CustID [varchar](255) NOT NULL
PRIMARY KEY (CustID)
)
CREATE TABLE [dbo].[Tblnewcustids](
CustID [varchar](255) NOT NULL
PRIMARY KEY (CustID)
)
CREATE TABLE [dbo].[TblMapping](
[CurCustID] [varchar](255) NOT NULL,
[NewCustID] [varchar](255) NOT NULL
PRIMARY KEY (CurCustID,NewCustID)
)

Monitor changes in a record spanning multiple related tables

I seek the help of the advance SQL gurus in here.
I have 5 Related tables: table A + B + C + D + E in which table A exists in B,C,D,E as a foreign key.
I further have one big table that combines all rows from all the 5 table, namely:
'table A + B + C + D + E'.
I'm looking for a way to monitor changes from either one of the 5 tables and insert the entire related record (A + B + C + D + E), be they UPDATES or INSERTS into the big table: 'table A + B + C + D + E'.
I'm still at level novice with my SQL programming.
Any help will be greatly appreciated.
OK, nobody's touched it in more than a day, and it is related to some exploration I've been doing into the interaction between triggers and transactions, so I'll tweak one of my experiments to (hopefully) make it apply to your problem.
But first, I'm not clear on exactly what you're doing so I'm going to make a few assumptions. I'll state them for clarity.
1) I assume you've considered using a view for the Big Table and rejected that for some reason. If you haven't, stop now and look into that, it's often the best solution for cases like this. You can even make a view based on multiple tables update-able by writing an INSTEAD OF trigger for it. It will always have the latest data without any timing issues or logic problems.
2) I assume you want changes APPENDED to your big table, as you said, though the change to UPDATE shouldn't be hard. Just bear in mind my solution is for APPEND.
3) I assume you don't care about deletes, since you specifically mention UPDATES and INSERTS
4) I assume that when you say foreign key relationship you mean a real formal one, and that it's a one to one relationship.
5) I assume you need the Big Table updated in real time, you could probably get a more robust solution if you just flagged dirty rows and did an hourly or nightly update based on that.
Trying to explain the solution is hard, so I'll give you a template that you can expand with your specific schema. The basic premise is to attach "after" triggers to the update and insert events for your component tables. Each trigger tries to combine its changed rows with the matching rows in A and write the whole thing to the big table (ABC). I only used 3 tables, you can easily extend to 5.
Note that because you have a foreign key relationship and the triggers are "after", you are assured that the A row exists or the FK constraint would have rolled back the transaction and not fired the trigger. So A is inner joined to a changed table, but the others are left outer joined.
First, create the base tables you'll be working with
USE [JUNK]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
--DROP TABLE A
--DROP TABLE B
--DROP TABLE C
--DROP TABLE ABC
GO
CREATE TABLE [dbo].[A](
[AID] [int] NOT NULL, --primary key, not enforced here to facilitate sample data entry
[ADat] [varchar](50) NOT NULL,
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[B](
[BID] [int] NOT NULL, --primary key
[AID] [int] NOT NULL, --foreign key on A, not enforced here to facilitate sample data entry
[BDat] [varchar](50) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[C](
[CID] [int] NOT NULL, --primary key
[AID] [int] NOT NULL, --foreign key on A, not enforced here to facilitate sample data entry
[CDat] [varchar](50) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[ABC](
[abcID] [int] IDENTITY(1,1) NOT NULL,
[AID] [int] NOT NULL, --foreign key on A, not enforced here to facilitate sample data entry
[ADat] [varchar](50) NULL,
[BDat] [varchar](50) NULL,
[CDat] [varchar](50) NULL,
CONSTRAINT [PK_ABC] PRIMARY KEY CLUSTERED
(
[abcID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Next, set up the triggers that will monitor the component tables and update the big table when one is changed
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Robert Sheahan
-- Create date: 2014-07-24
-- Description: Testing multiple trigger interaction in transactions,
-- logs changes to a distributed record to a big table.
-- =============================================
CREATE TRIGGER dbo.trA_IU
ON dbo.A
AFTER INSERT,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT INTO ABC (AID, ADat, BDat, CDat)
SELECT A.AID, ADat, BDat, CDat
FROM A
LEFT OUTER JOIN B on A.AID = B.AID
LEFT OUTER JOIN C on A.AID = C.AID
END
GO
-- DROP TRIGGER dbo.trB_IU
CREATE TRIGGER dbo.trB_IU
ON dbo.B
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO ABC (AID, ADat, BDat, CDat)
SELECT A.AID, ADat, BDat, CDat
FROM A
INNER JOIN B on A.AID = B.AID
LEFT OUTER JOIN C on A.AID = C.AID
END
GO
-- DROP TRIGGER dbo.trC_IU; GO;
CREATE TRIGGER dbo.trC_IU
ON dbo.C
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO ABC (AID, ADat, BDat, CDat)
SELECT A.AID, ADat, BDat, CDat
FROM A
INNER JOIN C on A.AID = C.AID
LEFT OUTER JOIN B on A.AID = B.AID
END
GO
Finally, test it and refine it to meet your specific needs.
INSERT INTO A (AID, ADat) VALUES (1,'Hello'),(2,'Goodbye')
INSERT INTO B (BID, AID, BDat) VALUES (11,1,'new'),(12,2,'cruel')
INSERT INTO C (CID, AID, CDat) VALUES (21,1,'world!'),(22,2,'world')
UPDATE B SET BDat = 'old' WHERE AID=1
UPDATE C SET CDat = 'planet' WHERE AID=1
SELECT * FROM ABC

How to split table into multiple tables using SQL

Hi I have this table Cars:
MODEL nvarchar(20)
STYLE nvarchar(20)
ENGINE nvarchar(5)
CAPACITY smallint
MAX_SPEED smallint
PRICE smallmoney
MARKET nvarchar(20)
COMPETITOR nvarchar(20)
And I would like to split it into 3 tables via SQL query:
Cars:
MODEL nvarchar(20)
STYLE nvarchar(20)
MAX_SPEED smallint
PRICE smallmoney
Engine:
ENGINE nvarchar(5)
CAPACITY smallint
Market:
MARKET nvarchar(20)
COMPETITOR nvarchar(20)
So was wandering how this would be done using sql commands, thanks
Easiest way. Select... Into will create new tables:
SELECT DISTINCT
ENGINE,
CAPACITY
INTO Engine
FROM CARS
SELECT DISTINCT
MARKET,
COMPETITOR
INTO Market
FROM CARS
Then just drop the defunct columns from the original table. Eg
ALTER TABLE Cars DROP COLUMN ENGINE
ALTER TABLE Cars DROP COLUMN CAPACITY
ALTER TABLE Cars DROP COLUMN MARKET
ALTER TABLE Cars DROP COLUMN COMPETITOR
This will do specifically what you are asking. However, I'm not sure that is what you want - there is then no reference from the car to the engine or market details - so information is lost.
If "ENGINE" and "MARKET" define the keys of the new table, I'd suggest leaving those columns on the car table as foreign keys. Eg only DROP Capacity and Competitor.
You may wish to create the primary key on the new tables too. Eg:
ALTER TABLE ENGINE ADD CONSTRAINT [PK_Engine] PRIMARY KEY CLUSTERED ENGINE ASC
Run this....
create table Engine
(
EngineId int identity(1,1) not null primary key,
Engine nvarchar(5) not null,
Capacity smallint not null
)
go
insert into Engine
(Engine, Capacity)
(select distinct Engine,Capacity from Cars)
go
alter table Cars
add EngineId int null
go
update Cars
set Cars.EngineId = e.EngineId
from Engine e where e.Engine = Cars.Engine
go
create table Market
(
Id int identity(1,1) not null primary key,
Market nvarchar(20) not null,
Competitor nvarchar(20) not null
)
go
insert into Market
(Market, Competitor)
(select distinct Market,Competitor from Cars)
go
alter table Cars
add MarketId int null
go
update Cars
set Cars.MarketId = m.MarketId
from Market m where m.Market = Cars.Market
go
alter table Cars
drop column Market;
alter table Cars
drop column Competitor;
alter table Cars
drop column Engine;
alter table Cars
drop column Capacity;
To normalize these tables you will firstly need to create new tables, write SQL to insert the data into the new tables and then alter the original table.
See http://technet.microsoft.com/en-us/library/ms174979.aspx
and http://technet.microsoft.com/en-us/library/dd776381(v=sql.105).aspx and http://msdn.microsoft.com/en-us/library/ms190273.aspx