我有一个表格,显示每个位置使用的每个工具的BIT列位置:
CREATE TABLE dbo.[ToolsSelected] (
[LocationID] NVARCHAR(40) NOT NULL,
[Tool1] INTEGER DEFAULT 0 NOT NULL,
[Tool2] INTEGER DEFAULT 0 NOT NULL,
[Tool3] INTEGER DEFAULT 0 NOT NULL,
[Tool4] INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY ([LocationID])
);
LocID Tool1 Tool2 Tool3 Tool4
----- ----- ----- ----- -----
AZ 0 1 1 0
NY 1 0 1 1
我需要通过LocationID将其转换为表格,以指示哪些位置的工具:
CREATE TABLE dbo.[ByLocation] (
[LocationID] NVARCHAR(40) NOT NULL,
[Tool] NVARCHAR(40) NOT NULL, -- Column title of ToolsSelected table
PRIMARY KEY ([LocationID], [Tool])
);
LocID Tool
----- -----
AZ Tool2
AZ Tool3
NY Tool1
NY Tool3
NY Tool4
我们的想法是每个位置都可以选择他们需要的工具,然后我需要查询工具表以获取所选每个工具的详细信息(版本等)。每个地点都是独特的;每个工具都是独特的。有没有办法做到这一点或更好的实施?
答案 0 :(得分:4)
以下问题的答案只有4个工具栏:
SELECT LocID = LocationID, Tool
FROM
(
SELECT LocationID, Tool = 'Tool1' FROM dbo.ToolsSelected WHERE Tool1 = 1
UNION ALL
SELECT LocationID, Tool = 'Tool2' FROM dbo.ToolsSelected WHERE Tool2 = 1
UNION ALL
SELECT LocationID, Tool = 'Tool3' FROM dbo.ToolsSelected WHERE Tool3 = 1
UNION ALL
SELECT LocationID, Tool = 'Tool4' FROM dbo.ToolsSelected WHERE Tool4 = 1
) AS x
ORDER BY LocID, Tool;
有40列,您可以做同样的事情,但同时也希望动态生成它:
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'';
SELECT @sql += '
UNION ALL
SELECT LocationID, Tool = ''' + name + '''
FROM dbo.ToolsSelected WHERE ' + name + ' = 1'
FROM sys.columns WHERE [object_id] = OBJECT_ID('dbo.ToolsSelected')
AND name LIKE 'Tool[0-9]%';
SELECT @sql = N'SELECT LocID = LocationID, Tool
FROM
(' + STUFF(@sql, 1, 17, '') + '
) AS x ORDER BY LocID, Tool;';
PRINT @sql;
-- EXEC sp_executesql @sql;
*的 BUT * 强>
将这些存储为单独的列是灾难的一个方法。因此,当您添加Tool41,Tool42等时,您必须更改架构,然后通过参数等更改所有传递列名称和1/0的代码。为什么不将这些代码表示为简单数字,例如
CREATE TABLE dbo.LocationTools
(
LocID NVARCHAR(40),
ToolID INT
);
所以在上面的例子中你会存储:
LocID Tool
----- ----
AZ 2
AZ 3
NY 1
NY 3
NY 4
现在,当您传入他们选中的复选框时,可能是从前端收到两个值,例如:
LocID: "NY"
Tools: "Tool1, Tool5, Tool26"
如果这是正确的,那么您可以在用户创建或更改其选择时填充表,首先使用拆分功能拆分由复选框指定的逗号分隔列表:
CREATE FUNCTION dbo.SplitTools
(
@ToolList NVARCHAR(MAX)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT ToolID = y.i.value('(./text())[1]', 'int')
FROM
(
SELECT x = CONVERT(XML,
'<i>' + REPLACE(REPLACE(@List, ',', '</i><i>'), 'Tool', '')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
(您忘了告诉我们您使用的是哪个版本的SQL Server - 如果是2008或更高版本,您可以使用表值参数作为拆分函数的替代方法。)
然后处理它的程序:
CREATE PROCEDURE dbo.UpdateLocationTools
@LocID NVARCHAR(40),
@Tools NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
-- in case they had previously selected tools
-- that are no longer selected, clear first:
DELETE dbo.LocationTools WHERE LocID = @LocID;
INSERT dbo.LocationTools(LocID, ToolID)
SELECT @LocID, ToolID
FROM dbo.SplitTools(@Tools);
END
GO
现在,您可以在不更改架构或代码的情况下添加新工具#,因为您的复选框列表也可以从您的数据中生成 - 假设您有一个dbo.Tools
表或想要添加一个表。此表还可用于数据完整性目的(您可以在dbo.LocationTools.ToolID
上放置外键)。
您可以非常简单地生成所需的查询:
SELECT LocID, Tool = 'Tool' + CONVERT(VARCHAR(12), ToolID)
FROM dbo.LocationTools
ORDER BY LocID, ToolID;
没有冗余数据,没有无法管理列的宽表以及正确的索引甚至可以帮助您有效地使用Tool3搜索所有位置......