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

tmj*_*tmj 17 sql t-sql sql-server

像这样的代码,但它错了:

CREATE PROC sp_createATable
  @name        VARCHAR(10),
  @properties  VARCHAR(500)
AS
  CREATE TABLE @name
  (
    id  CHAR(10)  PRIMARY KEY,
    --...Properties extracted from @properties
  );
Run Code Online (Sandbox Code Playgroud)

你能告诉我怎么处理吗?这真让我烦恼.

Nam*_*ian 44

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

你创建一个像这样的临时表:

CREATE TABLE #customer
(
     Name varchar(32) not null
)
Run Code Online (Sandbox Code Playgroud)

你声明一个表变量,如下所示:

DECLARE @Customer TABLE
(
      Name varchar(32) not null
)
Run Code Online (Sandbox Code Playgroud)

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

更新:

根据您在下面的评论,您实际上是在尝试在存储过程中创建表.为此,您需要使用动态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
Run Code Online (Sandbox Code Playgroud)

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

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 
Run Code Online (Sandbox Code Playgroud)

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

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

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

更新2:

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

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

  1. 这是一个拥有许多商店的电子商务网站
  2. 商店可以出售许多物品(商品).
  3. 特定商品(商品)可以在许多商店出售
  4. 商店将针对不同的商品(商品)收取不同的价格
  5. 所有价格均为(美元)

假设这个电子商务网站销售游戏机(即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
Run Code Online (Sandbox Code Playgroud)

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

INSERT INTO Shop
SELECT 'American Games R US'
UNION
SELECT 'Europe Gaming Experience'
UNION
SELECT 'Asian Games Emporium'
Run Code Online (Sandbox Code Playgroud)

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

ShopID  ShopName
1           American Games R US
2           Asian Games Emporium
3           Europe Gaming Experience
Run Code Online (Sandbox Code Playgroud)

是的,现在让我们转到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
Run Code Online (Sandbox Code Playgroud)

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

INSERT INTO Product
SELECT 'Wii'
UNION 
SELECT 'PS3'
UNION 
SELECT 'XBOX360'
Run Code Online (Sandbox Code Playgroud)

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

ProductID   ProductDescription
1           PS3
2           Wii
3           XBOX360
Run Code Online (Sandbox Code Playgroud)

好的,此时您同时拥有产品和商店信息.那你怎么把它们组合在一起呢?我们知道我们可以通过其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
Run Code Online (Sandbox Code Playgroud)

因此,我们假设American Games R Us商店仅销售美国游戏机,Europe Gaming Experience销售所有游戏机,而Asian Games Emporium仅销售亚洲游戏机.我们需要将商店和产品表中的主键映射到ShopProduct表中.

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

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

INSERT INTO ShopProduct VALUES(1,3,159.99)
Run Code Online (Sandbox Code Playgroud)

现在我们想要将所有产品添加到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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

您可能会看到以下结果:

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
Run Code Online (Sandbox Code Playgroud)

现在,最后一个例子,我们假设您的网站有一个功能,可以找到最便宜的控制台价格.用户要求查找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
Run Code Online (Sandbox Code Playgroud)

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

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

我希望这可以帮助您在数据库设计中旅行.