我正在努力将来自第三方系统的一些数据集成到我的一个应用程序中(传统的基于ASP Classic的Web应用程序/ SQL 2000) - 当他们采用他们的方法时,他们做出了一些糟糕的决定(恕我直言)和数据结构,虽然也许我们可能有机会在某些时候进行重构......但在那之前,我必须使用在我面前的东西。
主表保存检查数据,其中一个字段用于记录是否观察到某些特征。这些特征存储在名为Categories
的表中,但遗憾的是,主要检查表(Test
)通过将相关CategoryID
连接到单个字段(SelectedCategories
来链接到类别})。因此,例如,如果观察到特征01和02,SelectedCategories
中该行的Test
列的值将为'01C02C'。
修剪DDL:
CREATE TABLE [dbo].[Test](
[ItemID] [varchar](255) NOT NULL,
[Result] [varchar](255) NULL,
[Comments] [varchar](255) NULL,
[ResultReason] [varchar](255) NULL,
[ImageLocation] [varchar](255) NULL,
[TestDateTime] [smalldatetime] NOT NULL,
[SelectedCategories] [varchar](255) NULL)
问题是,鉴于这种情况,我如何才能最好地从Test
中提取数据,并对所观察到的特征进行细分?
我想要的客户端上的输出是一个包含以下列的表: Test.PK,Test.Field2 ... Test.Fieldn,Categories.ID1,Categories.ID2,Categories.IDn
这可能不够清楚 - 第一个字段是来自Test
的常见嫌疑人,后面是Categories
中每个类别的勾号或交叉(或其他视觉指示符)。
显然,如果这可以在一个查询中实现,那么在效率和性能方面就更好了。但是,我不确定如何实现这一目标 - 您将如何通过Categories
加入SelectedCategories
表格?
我显然可以简单地报告SelectedCategories
值并让应用程序解析该值。这可能是硬编码的,或者我们更有可能为Test中的每一行重新查询Categories
- 尽管这会产生性能影响。 TBH,在这种情况下表现可能不是问题,但仅仅因为你可以逃避某些事情,并不意味着你应该养成它的习惯。
同样,如果我有机会重构第三方应用程序,我会删除SelectedCategories
列并添加TestCategories
表吗?或者我会将每个类别硬编码为一系列Bit列。很有可能,Categories
在系统的整个生命周期中都不会发生变化,但如果它们发生了变化,则意味着DB和应用程序都会发生变化(尽管很小)。
我希望我已经足够清楚地解释了它。从本质上讲,我说如果我坚持使用当前的系统,最好的方法是什么?如果我要重构,我可以采取什么不同的方法?
进度更新:
非常感谢列文,我到目前为止:
DML:
SELECT c.ID, c.Category, t.FilterID, t.OperatorResult, t.SelectedCategories
FROM dbo.Categories c
inner JOIN dbo.Test t ON CHARINDEX(Cast(c.ID as varchar), t.SelectedCategories, 1) <> 0
order by FilterID, ID
输出:
ID Category FilterID OperatorResult SelectedCategories
4 Cracked Ceramic 137667 FAILED 04C
4 Cracked Ceramic 284821 FAILED 04C
4 Cracked Ceramic 287617 FAILED 04C05C
5 Damaged Case 287617 FAILED 04C05C
4 Cracked Ceramic 310112 FAILED 04C05C
5 Damaged Case 310112 FAILED 04C05C
这就足够了,除了为了达到我想要的屏幕输出......
Filter ID Operator Result Cat Matl Crack Damage High Soot
137667 FAILED X X
178643 FAILED
284821 FAILED X
287617 FAILED X X
310112 FAILED X X
...我要么需要进一步研究SQL(这样我只需要在一个查询中实现所需的输出),或者我需要在应用程序本身做一些额外的工作。
结论:
如果我们看一下Lieven的最新例子(如下),我们可以看到问题可以在TSQL中解决,但是类别是硬编码的。
替代方案是坚持原始数据并使IIS / ASP做更多工作。它会使源代码复杂化,但如果添加或删除类别,将消除更新TSQL的潜在开销。我当然可以忍受更新TSQL这种非常偶然的需求,但我正在考虑类别表将定期主动更改的不同问题。
答案 0 :(得分:3)
通过Categories
加入SelectedCategories
表可以像这样工作
要考虑的一些事情
让我们知道它是否适合您。
BEGIN TRAN
CREATE TABLE [dbo].[Categories](
[CategorieID] INTEGER NOT NULL)
CREATE TABLE [dbo].[Test](
[ItemID] [varchar](255) NOT NULL,
[Result] [varchar](255) NULL,
[Comments] [varchar](255) NULL,
[ResultReason] [varchar](255) NULL,
[ImageLocation] [varchar](255) NULL,
[TestDateTime] [smalldatetime] NOT NULL,
[SelectedCategories] [varchar](255) NULL)
INSERT INTO dbo.Categories VALUES (4)
INSERT INTO dbo.Categories VALUES (5)
INSERT INTO dbo.Test VALUES (137667, 'FAILED', NULL, 'Cracked Ceramic', NULL, GetDate(), '04C')
INSERT INTO dbo.Test VALUES (284821, 'FAILED', NULL, 'Cracked Ceramic', NULL, GetDate(), '04C')
INSERT INTO dbo.Test VALUES (287617, 'FAILED', NULL, 'Cracked Ceramic', NULL, GetDate(), '04C05C')
INSERT INTO dbo.Test VALUES (287617, 'FAILED', NULL, 'Damaged Case' , NULL, GetDate(), '04C05C')
INSERT INTO dbo.Test VALUES (310112, 'FAILED', NULL, 'Cracked Ceramic', NULL, GetDate(), '04C05C')
INSERT INTO dbo.Test VALUES (310112, 'FAILED', NULL, 'Damaged Case' , NULL, GetDate(), '04C05C')
SELECT [Filter ID] = t.ItemID
, [Operator Result] = t.Result
, [Reason] = t.ResultReason
INTO #Output
FROM dbo.Categories c
LEFT OUTER JOIN dbo.Test t ON
/* Search for "C<{00}CategorieID>C" */
CHARINDEX('C' -- Prefix CategorieID & SelectedCategories with 'C'
+ REPLICATE('0', 2 - LEN(CAST(CategorieID AS VARCHAR))) -- Left Pad CategorieID with '0'
+ CAST(CategorieID AS VARCHAR) -- Add CategorieID itself
+ 'C' -- Suffix search string with 'C'.
, 'C' + t.SelectedCategories -- Prefix CategorieID & SelectedCategories with 'C'
, 1) <> 0
SELECT [Filter ID]
, [Operator Result]
, [Cat Matl] = CASE WHEN [Cat Matl] = 1 THEN 'X' ELSE '' END
, [Crack] = CASE WHEN [Crack] = 1 THEN 'X' ELSE '' END
, [Damage] = CASE WHEN [Damage] = 1 THEN 'X' ELSE '' END
, [High Soot] = CASE WHEN [High Soot] = 1 THEN 'X' ELSE '' END
FROM (
SELECT [Filter ID]
, [Operator Result]
, [Cat Matl] = MAX(CASE WHEN Reason = 'Cat Matl' THEN 1 ELSE 0 END)
, [Crack] = MAX(CASE WHEN Reason = 'Cracked Ceramic' THEN 1 ELSE 0 END)
, [Damage] = MAX(CASE WHEN Reason = 'Damaged Case' THEN 1 ELSE 0 END)
, [High Soot] = MAX(CASE WHEN Reason = 'High Soot' THEN 1 ELSE 0 END)
FROM #Output
GROUP BY [Filter ID]
, [Operator Result]
) o
DROP TABLE #Output
ROLLBACK TRAN