T-Sql查询具有动态(未知)列数

时间:2016-12-09 09:39:08

标签: sql sql-server tsql pivot-table

我们有一个项目,我们应该为用户提供将自己的自定义列添加到各种表的可能性。

编辑:这些是2个表,而不是一个。

**Products**
ProductId
Name
Price
Date
UserId

**ProductsCustomColumns**
ProductId
ColumnName
ColumnValue

编辑:请注意,动态列会记录为值,我们不知道这些值的计数......它可以是0或200或任何。

这是一个例子: enter image description here 现在,当我们查询产品表时,我们要显示所有预定义列,然后是所有自定义列。 显然,每个用户都可以拥有自己的带有值和名称的列数。

SELECT *, (and the custom columns) FROM Products WHERE UserId = 3 AND ProductId = 1

以下是2个问题:

  1. 从性能的角度来看,这是一个很好的解决方案还是有更好的解决动态列要求的方法?

  2. 如何创建一个查询,可以读取ProductsCustomColumns给定userIdproductId的所有记录,并将记录作为列附加到查询中?

  3. 感谢。

3 个答案:

答案 0 :(得分:2)

您需要编写动态查询

DECLARE @COLUMNS VARCHAR(MAX)='', @QRY VARCHAR(MAX);

SELECT  @COLUMNS = @COLUMNS +COLUMN_NAME +',' FROM 
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='Products'

SELECT @COLUMNS =SUBSTRING (@COLUMNS,1 ,LEN(@COLUMNS)-1)

SELECT @QRY ='SELECT '+@COLUMNS + ' FROM Products WHERE UserId = 3 AND ProductId = 1' 
EXEC (@QRY)

编辑:来自您的评论&编辑问题

我从你的问题中假设的模式

CREATE TABLE Products (
ProductId INT,
Name VARCHAR(250),
Price DECIMAL(18,2),
DateS DATETIME,
UserId INT)

INSERT INTO Products
SELECT 1,'Oil Product', 2000, GETDATE(), 3
UNION ALL
SELECT 2,'Amway', 600, GETDATE(), 2
UNION ALL
SELECT 3,'Thermal', 5000, GETDATE(), 1
UNION ALL
SELECT 4,'Oil Product', 500, GETDATE(), 4



CREATE TABLE ProductsCustomColumns
(
ProductId INT ,
ColumnName VARCHAR(200),
ColumnValue VARCHAR(15))

INSERT INTO ProductsCustomColumns
SELECT 1, 'Licence_No', '1545'
UNION ALL
SELECT 1, 'Location ', 'Atlanta'
UNION ALL
SELECT 2, 'Qty ', '5'
UNION ALL
SELECT 3, 'Gross', '80000'

现在你的动态代码就在这里

DECLARE  @COLUMN_PCC VARCHAR(MAX)='',  @PRODUCT_ID INT=1,@USER_ID INT=3, @QRY VARCHAR(MAX) ;

--preparing Custom Column Name List with comma ','
SELECT @COLUMN_PCC = @COLUMN_PCC+ [COLUMNNAME]  +',' FROM ProductsCustomColumns 
WHERE ProductId= @PRODUCT_ID
SELECT @COLUMN_PCC =SUBSTRING(@COLUMN_PCC,1,LEN(@COLUMN_PCC)-1)


--Preparing Dynamic Query
SELECT @QRY =' SELECT P.*, AV.* FROM Products P
INNER JOIN 
(
SELECT * FROM (
SELECT * FROM ProductsCustomColumns WHERE ProductId= '+CAST(@PRODUCT_ID AS VARCHAR(50))+'
)
AS A
PIVOT 
(
MAX (COLUMNVALUE)
FOR [COLUMNNAME] IN ('+@COLUMN_PCC +')
)AS PVT
)AS AV ON P.ProductId= AV.ProductId 
AND P.UserId='++CAST(@USER_ID AS VARCHAR(50))+'
'

EXEC ( @QRY)

结果将是

 +-----------+-------------+---------+-------------------------+--------+-----------+------------+----------+
| ProductId |    Name     |  Price  |          DateS          | UserId | ProductId | Licence_No | Location |
+-----------+-------------+---------+-------------------------+--------+-----------+------------+----------+
|         1 | Oil Product | 2000.00 | 2016-12-09 18:06:24.090 |      3 |         1 |       1545 | Atlanta  |
+-----------+-------------+---------+-------------------------+--------+-----------+------------+----------+

答案 1 :(得分:1)

通常,在主表的其他列中添加自定义数据是一个非常糟糕的主意。想象一下有100个客户使用它。它们都有不同的表模式,您可以为所有这些模式编写更新脚本吗?

如果您必须事先处理不了解结构的结果集,这是一个痛苦的问题!

您有多种选择:

  • 添加一列XML类型的列。优点:结果集已修复。您只需要一个客户特定的规则,如何解释XML。您可以使用内联表值函数来解决此问题。传入XML并返回派生表。请使用CROSS APPLY调用此信息,然后您就出局了......

  • 添加一个包含customerID和Key-Value-Pairs的新表

  • 如果其他数据不完全不同,请将一些列添加到主表中SPARSE

答案 2 :(得分:0)

你需要动态sql,没有其他方法可以做到这一点

DECLARE @sql      VARCHAR(max),
        @cust_col VARCHAR(max)

SET @cust_col = (SELECT Quotename(CustomColumns) + ','
                 FROM   ProductsCustomColumns
                 FOR xml path(''))

SELECT @cust_col = LEFT(@cust_col, Len(@cust_col) - 1)

SET @sql = 'SELECT *, ' + @cust_col + ' FROM Products WHERE UserId = 3 AND ProductId = 1'

EXEC (@sql)