T-SQL如何在存储过程中动态创建表?

时间:2012-06-04 06:56:05

标签: sql sql-server tsql

这样的代码,但是错了:

CREATE PROC sp_createATable
  @name        VARCHAR(10),
  @properties  VARCHAR(500)
AS
  CREATE TABLE @name
  (
    id  CHAR(10)  PRIMARY KEY,
    --...Properties extracted from @properties
  );
你能告诉我怎么处理吗?这真让我烦恼。

5 个答案:

答案 0 :(得分:42)

您正在使用表变量,即您应该声明该表。这不是临时表。

您可以像这样创建一个临时表:

CREATE TABLE #customer
(
     Name varchar(32) not null
)

你声明一个表变量如下:

DECLARE @Customer TABLE
(
      Name varchar(32) not null
)

请注意,临时表是使用#声明的,而表变量是使用@声明的。 去了解表变量和临时表之间的区别。

<强>更新

根据您在下面的评论,您实际上是在尝试在存储过程中创建表。为此,您需要使用动态SQL。基本上,动态SQL允许您以字符串的形式构造SQL语句,然后执行它。这是您能够在存储过程中创建表的唯一方法。我将向您展示如何,然后讨论为什么这通常不是一个好主意。

现在举一个简单的例子(我没有测试过这段代码,但是它应该给你一个很好的指示:)

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

这个存储过程可以像这样执行:

sproc_BuildTable 'Customers','CustomerName','VARCHAR(32)','NOT NULL'

此类存储过程存在一些主要问题。

很难满足复杂的表格需求。想象一下下面的表结构:

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 

这个表比第一个例子稍微复杂一点,但不是很多。处理存储过程会非常复杂得多。因此,虽然这种方法可能适用于小型表,但很快就会无法管理。

创建表需要规划。创建表时,应将它们策略性地放在不同的文件组中。这是为了确保您不会导致磁盘I / O争用。如果在主文件组上创建了所有内容,您将如何解决可伸缩性问题?

您能澄清为什么需要动态创建表格吗?

更新2:

由于工作量导致延迟更新。我阅读了你关于需要为每个商店创建一个表的评论,我认为你应该像我即将给你的例子那样去做。

在这个例子中,我做了以下假设:

  1. 这是一家拥有众多商店的电子商务网站
  2. 商店可以出售许多商品(商品)。
  3. 特定商品(商品)可在许多商店出售
  4. 商店将针对不同商品(商品)收取不同的价格
  5. 所有价格均为$(USD)
  6. 假设这个电子商务网站销售游戏机(即Wii,PS3,XBOX360)。

    看看我的假设,我看到了经典的多对多关系。商店可以出售许多商品(商品),商品(商品)可以在许多商店出售。让我们把它分解成表格。

    首先,我需要一个商店桌来存储有关商店的所有信息。

    一个简单的商店表可能如下所示:

    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
    

    让我们在数据库中插入三个商店,以便在我们的示例中使用。以下代码将插入三个商店:

    INSERT INTO Shop
    SELECT 'American Games R US'
    UNION
    SELECT 'Europe Gaming Experience'
    UNION
    SELECT 'Asian Games Emporium'
    

    如果执行SELECT * FROM Shop,您可能会看到以下内容:

    ShopID  ShopName
    1           American Games R US
    2           Asian Games Emporium
    3           Europe Gaming Experience
    

    是的,现在让我们进入Items(商品)表。由于物品/商品是各种公司的产品,我打算称之为产品。您可以执行以下代码来创建简单的Product表。

    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
    

    让我们用一些产品填充产品表。执行以下代码以插入一些产品:

    INSERT INTO Product
    SELECT 'Wii'
    UNION 
    SELECT 'PS3'
    UNION 
    SELECT 'XBOX360'
    

    如果执行SELECT * FROM Product,您可能会看到以下内容:

    ProductID   ProductDescription
    1           PS3
    2           Wii
    3           XBOX360
    

    好的,此时您同时拥有产品和商店信息。那你怎么把它们组合在一起呢?我们知道我们可以通过其ShopID主键列识别商店,我们知道我们可以通过其ProductID主键列识别产品。此外,由于每个商店的每个产品的价格都不同,我们需要存储商店为产品收取的价格。

    因此我们有一张表将商店映射到产品。我们将此表称为ShopProduct。此表的简单版本可能如下所示:

    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
    

    因此,让我们假设美国运动会R Us商店只销售美国游戏机,欧洲游戏体验销售所有游戏机,亚运商场仅销售亚洲游戏机。我们需要将商店和产品表中的主键映射到ShopProduct表中。

    以下是我们如何进行映射。在我的例子中,American Games R Us的ShopID值为1(这是主键值),我可以看到XBOX360的值为3,而商店已经将XBOX360列为159.99美元

    通过执行以下代码,您将完成映射:

    INSERT INTO ShopProduct VALUES(1,3,159.99)
    

    现在我们要将所有产品添加到Europe Gaming Experience商店。在这个例子中,我们知道Europe Gaming Experience商店的ShopID为3,因为它销售所有控制台,我们需要将ProductID 1,2和3插入到映射表中。让我们假设欧洲游戏体验店的游戏机(产品)价格如下:1- PS3售价259.99美元,2- Wii售价159.99美元,3- XBOX360售价199.99美元。

    要完成此映射,您需要执行以下代码:

    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
    

    此时您已将两家商店及其产品映射到映射表中。好的,那么现在如何将这些全部组合在一起以显示用户浏览网站?假设您想要在网页上向用户显示欧洲游戏体验的所有产品 - 您需要执行以下查询:

    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
    

    您可能会看到以下结果:

    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
    

    现在,最后一个例子,让我们假设您的网站有一个功能,可以找到最便宜的控制台价格。用户要求找到最便宜的XBOX360。

    您可以执行以下查询:

     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
    

    此查询将返回所有商店的清单,这些商店首先以最便宜的商店销售XBOX360,依此类推。

    您会注意到我没有添加亚运商店。作为练习,使用以下产品将亚洲游戏商店添加到地图表中: 亚运商场以99.99美元的价格出售Wii游戏机,以159.99美元的价格出售PS3游戏机。如果您通过此示例,您现在应该了解如何建立多对多关系的模型。

    我希望这可以帮助您进行数据库设计。

答案 1 :(得分:2)

您需要从输入构建该CREATE TABLE语句,然后执行它。

一个简单的例子:

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);

答案 2 :(得分:2)

首先,您似乎在混合表变量和表。

无论哪种方式,你都不能传递表格的名称。您必须使用动态TSQL来执行此操作。

如果您只想声明一个表变量:

CREATE PROC sp_createATable 
  @name        VARCHAR(10), 
  @properties  VARCHAR(500) 
AS 
  declare @tablename TABLE
  ( 
    id  CHAR(10)  PRIMARY KEY 
  ); 

您希望创建存储过程以动态创建表的事实可能表明您的设计是错误的。

答案 3 :(得分:1)

这是一种使用T-SQL存储过程动态创建表的方法:

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);';

执行:

exec(@cmd);

答案 4 :(得分:0)

您可以编写以下代码: -

create procedure spCreateTable
   as
    begin
       create table testtb(Name varchar(20))
    end

执行它: -

exec spCreateTable