Sql-Server

在選擇解決方案和了解何時添加列與 EAV 時需要幫助

  • June 25, 2019

我長期以來一直在解決以下問題,但無法在兩種解決方案之間做出決定。雖然兩者都可以解決問題,但對我來說都沒有“感覺”完全正確。

對此的任何建議將不勝感激。另請注意,我對數據庫設計並不是特別擅長,因此感謝您提供任何回饋。

我有下表:

ProductYearDistrictCrop(+-200 萬條記錄)

oid | productId | yearId | districId     | cropId | payDate     | 
1   | 1         | 1      | 1             |1       | 2018-01-01  |
2   | 1         | 1      | 1             |1       | 2018-02-01  |
3   | 2         | 1      | 1             |1       | 2018-03-01  |
4   | 2         | 1      | 1             |1       | 2018-04-01  |

桌子上的一些背景資訊:

為 Product - Year - District - Crop 的每個可能組合創建一條記錄,並且對這些外鍵有唯一約束(該組合必須是唯一的)。我選擇使用 Oid 作為 PK 而不是複合鍵,因為 Oid 可以被其他表用作 FK,並且我認為查找 Oid 會比複合鍵更快。

對於這些記錄中的每一個,都可以選擇其他選項。這些選項來自下表

選項(16 條記錄)

oid | description   | systemDefaultValue
1   | Option 1      | 0.5 
2   | Option 2      | 2.5
3   | Option 3      | 1.5
4   | Option 4      | 1.5

對於這些選擇中的每一個,我需要儲存一個自定義值,或者只在 ProductYearDistrictCrop 中重用每條記錄的systemDefaultValue

我想出的兩個可能的解決方案是

解決方案 A:簡單地將每個可能的選項作為列添加到表中

oid | productId | yearId | districId     | cropId | payDate     | Option1   | Option2   | 
1   | 1         | 1      | 1             |1       | 2018-01-01  | 0.5       | 2.5       |
2   | 1         | 1      | 1             |1       | 2018-02-01  | 0.5       | 2.5       |
3   | 2         | 1      | 1             |1       | 2018-03-01  | 0.5       | 2.5       |
4   | 2         | 1      | 1             |1       | 2018-04-01  | 0.5       | 2.5       |

這意味著我們最終將在ProductYearDistrictCrop中增加 16 列,當然,每次向系統添加新選項時,我也需要更新此表。雖然這不會經常發生,但它可能會發生。

另一個考慮因素是還有其他表也將遵循類似的設計,這意味著每次添加新選項時我都必須更新幾個表。

解決方案 B:通過創建連結表,使用我認為是EAV方法的選項

ProductYearDistrictCropOptionChoices

ProductYearDistrictCrop  | optionId  | customValue |
1                        | 1         | 0.5         |
2                        | 2         | 1.5         |
3                        | 3         | 100         |
4                        | 4         | 25          |

在這種情況下,我最終得到了 +- 3200 萬條記錄。明顯的好處是每次添加新選項時都不需要更改數據庫。此外,我正在處理最壞的情況,因此ProductYearDistrictCrop中的每條記錄都不會有 16 條記錄與之關聯。

我的回饋

我的直覺是選擇解決方案 A並為每個選項添加列。然後,我可以在需要數據時進行一次查找,並從該行或多組行中獲取所需的一切。

我更喜歡解決方案 A的另一個原因是,據我所知,屬性應該是數據庫中的列。只是另一個表已經將這些屬性儲存為系統在建構 UI 並為 hte 使用者提供預設選項和值時使用的選項。

我已經為此苦苦掙扎了很長時間,只是想獲得一些外部建議和見解。此外,這將有助於獲得更多意見,因為我想用比“我覺得這個很好,那個很噁心”更強有力的理由來證明我的選擇是正確的

理查德,

我真的認為這是你提出的一個很好的問題。我的回答比我預期的要長,所以總而言之,根據您的要求,我有 3 種可能的解決方案。如果我必須對我的偏好進行排名,我會這樣做:

  1. EVA桌子
  2. 多個子表
  3. 單個“擴展”表

EVA方法

多虧了 Jon 的一些見解,這EVA當然是一種非常可行的方法,使用適當的索引PIVOT

為了PIVOT(因為我不熟悉它)上的範例,我創建了一個簡單的EAV表。

DECLARE @Table TABLE
(
   ID INT NOT NULL,
   OptionID INT NOT NULL,
   [Value] INT NULL
)

然後我用 16 個獨特ID的 ’s 填充它,每個配置為 16 個不同OptionID的 ‘s。該值由 和 的乘積填充,ID如果OptionID這兩個值相同,則沒有提供任何值(如果 ID <= 6NULL提供了一個值,否則沒有添加任何行(這樣我們就得到了一個NULL值和一個缺少行))。

INSERT INTO @Table (ID, OptionID, Value)
VALUES (1, 1, NULL),
(1, 2, 2),
(1, 3, 3),
(1, 4, 4),
(1, 5, 5),
(1, 6, 6),
(1, 7, 7),
(1, 8, 8),
(1, 9, 9),
(1, 10, 10),
(1, 11, 11),
(1, 12, 12),
(1, 13, 13),
(1, 14, 14),
(1, 15, 15),
(1, 16, 16),
(2, 1, 2),
(2, 2, NULL),
(2, 3, 6),
(2, 4, 8),
(2, 5, 10),
(2, 6, 12),
(2, 7, 14),
(2, 8, 16),
(2, 9, 18),
(2, 10, 20),
(2, 11, 22),
(2, 12, 24),
(2, 13, 26),
(2, 14, 28),
(2, 15, 30),
(2, 16, 32),
(3, 1, 3),
(3, 2, 6),
(3, 3, NULL),
(3, 4, 12),
(3, 5, 15),
(3, 6, 18),
(3, 7, 21),
(3, 8, 24),
(3, 9, 27),
(3, 10, 30),
(3, 11, 33),
(3, 12, 36),
(3, 13, 39),
(3, 14, 42),
(3, 15, 45),
(3, 16, 48),
(4, 1, 4),
(4, 2, 8),
(4, 3, 12),
(4, 4, NULL),
(4, 5, 20),
(4, 6, 24),
(4, 7, 28),
(4, 8, 32),
(4, 9, 36),
(4, 10, 40),
(4, 11, 44),
(4, 12, 48),
(4, 13, 52),
(4, 14, 56),
(4, 15, 60),
(4, 16, 64),
(5, 1, 5),
(5, 2, 10),
(5, 3, 15),
(5, 4, 20),
(5, 5, NULL),
(5, 6, 30),
(5, 7, 35),
(5, 8, 40),
(5, 9, 45),
(5, 10, 50),
(5, 11, 55),
(5, 12, 60),
(5, 13, 65),
(5, 14, 70),
(5, 15, 75),
(5, 16, 80),
(6, 1, 6),
(6, 2, 12),
(6, 3, 18),
(6, 4, 24),
(6, 5, 30),
(6, 6, NULL),
(6, 7, 42),
(6, 8, 48),
(6, 9, 54),
(6, 10, 60),
(6, 11, 66),
(6, 12, 72),
(6, 13, 78),
(6, 14, 84),
(6, 15, 90),
(6, 16, 96),
(7, 1, 7),
(7, 2, 14),
(7, 3, 21),
(7, 4, 28),
(7, 5, 35),
(7, 6, 42),
--(7, 7, NULL),
(7, 8, 56),
(7, 9, 63),
(7, 10, 70),
(7, 11, 77),
(7, 12, 84),
(7, 13, 91),
(7, 14, 98),
(7, 15, 105),
(7, 16, 112),
(8, 1, 8),
(8, 2, 16),
(8, 3, 24),
(8, 4, 32),
(8, 5, 40),
(8, 6, 48),
(8, 7, 56),
--(8, 8, NULL),
(8, 9, 72),
(8, 10, 80),
(8, 11, 88),
(8, 12, 96),
(8, 13, 104),
(8, 14, 112),
(8, 15, 120),
(8, 16, 128),
(9, 1, 9),
(9, 2, 18),
(9, 3, 27),
(9, 4, 36),
(9, 5, 45),
(9, 6, 54),
(9, 7, 63),
(9, 8, 72),
--(9, 9, NULL),
(9, 10, 90),
(9, 11, 99),
(9, 12, 108),
(9, 13, 117),
(9, 14, 126),
(9, 15, 135),
(9, 16, 144),
(10, 1, 10),
(10, 2, 20),
(10, 3, 30),
(10, 4, 40),
(10, 5, 50),
(10, 6, 60),
(10, 7, 70),
(10, 8, 80),
(10, 9, 90),
--(10, 10, NULL),
(10, 11, 110),
(10, 12, 120),
(10, 13, 130),
(10, 14, 140),
(10, 15, 150),
(10, 16, 160),
(11, 1, 11),
(11, 2, 22),
(11, 3, 33),
(11, 4, 44),
(11, 5, 55),
(11, 6, 66),
(11, 7, 77),
(11, 8, 88),
(11, 9, 99),
(11, 10, 110),
--(11, 11, NULL),
(11, 12, 132),
(11, 13, 143),
(11, 14, 154),
(11, 15, 165),
(11, 16, 176),
(12, 1, 12),
(12, 2, 24),
(12, 3, 36),
(12, 4, 48),
(12, 5, 60),
(12, 6, 72),
(12, 7, 84),
(12, 8, 96),
(12, 9, 108),
(12, 10, 120),
(12, 11, 132),
--(12, 12, NULL),
(12, 13, 156),
(12, 14, 168),
(12, 15, 180),
(12, 16, 192),
(13, 1, 13),
(13, 2, 26),
(13, 3, 39),
(13, 4, 52),
(13, 5, 65),
(13, 6, 78),
(13, 7, 91),
(13, 8, 104),
(13, 9, 117),
(13, 10, 130),
(13, 11, 143),
(13, 12, 156),
--(13, 13, NULL),
(13, 14, 182),
(13, 15, 195),
(13, 16, 208),
(14, 1, 14),
(14, 2, 28),
(14, 3, 42),
(14, 4, 56),
(14, 5, 70),
(14, 6, 84),
(14, 7, 98),
(14, 8, 112),
(14, 9, 126),
(14, 10, 140),
(14, 11, 154),
(14, 12, 168),
(14, 13, 182),
--(14, 14, NULL),
(14, 15, 210),
(14, 16, 224),
(15, 1, 15),
(15, 2, 30),
(15, 3, 45),
(15, 4, 60),
(15, 5, 75),
(15, 6, 90),
(15, 7, 105),
(15, 8, 120),
(15, 9, 135),
(15, 10, 150),
(15, 11, 165),
(15, 12, 180),
(15, 13, 195),
(15, 14, 210),
--(15, 15, NULL),
(15, 16, 240),
(16, 1, 16),
(16, 2, 32),
(16, 3, 48),
(16, 4, 64),
(16, 5, 80),
(16, 6, 96),
(16, 7, 112),
(16, 8, 128),
(16, 9, 144),
(16, 10, 160),
(16, 11, 176),
(16, 12, 192),
(16, 13, 208),
(16, 14, 224),
(16, 15, 240)--,
--(16, 16, NULL)

然後可以使用PIVOT命令查詢該表。(據我了解PIVOT,我們需要使用Aggregate函式SUM, MIN,MAX等……如果添加了一個約束以使ID-OptionID組合是唯一的,我們不應該得到任何不正確的輸出。我只是隨意SUM選擇

我不熟悉該PIVOT函式的性能如何,但它是一個內置函式,所以我想性能相當不錯。

SELECT ID, 
[1] AS Option1, --SUM([Value]) where OptionID = 1 Grouped By ID
[2] AS Option2, --SUM([Value]) where OptionID = 2 Grouped By ID
[3] AS Option3, --SUM([Value]) where OptionID = 3 Grouped By ID
[4] AS Option4, --SUM([Value]) where OptionID = 4 Grouped By ID
[5] AS Option5, --SUM([Value]) where OptionID = 5 Grouped By ID
[6] AS Option6, --SUM([Value]) where OptionID = 6 Grouped By ID
[7] AS Option7, --SUM([Value]) where OptionID = 7 Grouped By ID
[8] AS Option8, --SUM([Value]) where OptionID = 8 Grouped By ID
[9] AS Option9, --SUM([Value]) where OptionID = 9 Grouped By ID
[10] AS Option10, --SUM([Value]) where OptionID = 10 Grouped By ID
[11] AS Option11, --SUM([Value]) where OptionID = 11 Grouped By ID
[12] AS Option12, --SUM([Value]) where OptionID = 12 Grouped By ID
[13] AS Option13, --SUM([Value]) where OptionID = 13 Grouped By ID
[14] AS Option14, --SUM([Value]) where OptionID = 14 Grouped By ID
[15] AS Option15, --SUM([Value]) where OptionID = 15 Grouped By ID
[16] AS Option16  --SUM([Value]) where OptionID = 16 Grouped By ID
FROM 
(
   SELECT ID, OptionID, [Value]
   FROM @Table
) UP
PIVOT (
       SUM([Value]) 
       FOR [OptionID] IN([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16]) --Values held in the OptionID column
   ) AS pvt
ORDER BY ID

這給你一個強大的融合:

  1. 易於添加新選項(所有這些程式碼都可以載入到VIEWCTE為了便於使用。添加新選項時,Option您只需在最終選擇中添加一個額外的 Column並在 的部分中[17] AS Option17添加)。, [17]``FOR [OptionID](...)``PIVOT
  2. CASE節省空間,因為只有當它們與預設值不同並且可以通過語句應用時才能添加記錄。
  3. EAV表真的很窄,所以只要表被索引好,性能應該是非常好的。

擴展表法

***當涉及到附加列方法時,***大多數人擔心的是它沒有被規範化。我有一次聽到關於“規範化直到它受傷。去規範化直到它起作用”的評論。(實際上有一個DBA Stack Exchange 主題涵蓋了這個想法,關於要走多遠Normalization)。考慮到這一切,如果這將成為數據庫的一個高度利用的部分,那麼這可能是採用稍微非規範化設計的候選者,出於性能原因可能是您想要做的。

但是,我不建議將列添加到現有表中。 表格越寬,同一頁面上可以容納的數據就越少,這意味著使用該表格的所有其他內容都會降低性能。解決這個問題的一種方法是創建一個“擴展”table,它除了保存這些值之外什麼都不做。您只需JOIN在這張表中獲取所有 16 個選項(無論它們是否始終被寫入,或者列是NULLABLE並且僅當它與預設值不同時才具有值)。添加其他Option內容時,您只需在此表中添加一個新列。

這張桌子,在空間方面確實效率低下。但是我們會犧牲這個空間,以便在不使用這個新擴展表的情況下以及在與它一起使用的SELECT情況下使用時的查詢性能。ProductYearDistrictCrop``ProductYearDistrictCrop

CREATE TABLE [dbo].[ProductYearDistrictCropOptions]
(
   [Oid] [BIGINT] NOT NULL,
   [Option1] [Decimal(10,5)] NULL, --Or Whatever the appropriate datatype is
   [Option2] [Decimal(10,5)] NULL, 
   [Option3] [Decimal(10,5)] NULL, 
   [Option4] [Decimal(10,5)] NULL, 
   [Option5] [Decimal(10,5)] NULL, 
   [Option6] [Decimal(10,5)] NULL, 
   [Option7] [Decimal(10,5)] NULL, 
   [Option8] [Decimal(10,5)] NULL, 
   [Option9] [Decimal(10,5)] NULL, 
   [Option10] [Decimal(10,5)] NULL, 
   [Option11] [Decimal(10,5)] NULL, 
   [Option12] [Decimal(10,5)] NULL, 
   [Option13] [Decimal(10,5)] NULL, 
   [Option14] [Decimal(10,5)] NULL, 
   [Option15] [Decimal(10,5)] NULL, 
   [Option16] [Decimal(10,5)] NULL, 

   CONSTRAINT [PK_ProductYearDistrictCropOptions] PRIMARY KEY CLUSTERED 
   (
       [Oid] ASC
   )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[ProductYearDistrictCropOptions]  WITH CHECK ADD  CONSTRAINT [FK_ProductYearDistrictCropOptions_ProductYearDistrictCrop] FOREIGN KEY([Oid])
REFERENCES [dbo].[ProductYearDistrictCrop] ([Oid])
GO

ALTER TABLE [dbo].[ProductYearDistrictCropOptions] CHECK CONSTRAINT [FK_ProductYearDistrictCropOptions_ProductYearDistrictCrop]
GO

多子表方法

您可以通過多個解決方案來解決空間使用率低的問題child tables

前幾天我正在閱讀一篇關於 NULLS的不同DBA Stack Exchange Question中的參考文章。本文建議可以使用多個持有值的子表來指定這一點。每個option人都有自己的child table保存這些數據的地方。您最終會創建 16 個版本,例如:

CREATE TABLE [dbo].[ProductYearDistrictCropOption1Values]
(
   [Oid] [BIGINT] NOT NULL,
   [Value] [Decimal(10,5)] NOT NULL, --Or Whatever the appropriate datatype is

   CONSTRAINT [PK_ProductYearDistrictCropOption1Values] PRIMARY KEY CLUSTERED 
   (
       [Oid] ASC
   )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[ProductYearDistrictCropOption1Values]  WITH CHECK ADD  CONSTRAINT [FK_ProductYearDistrictCropOption1Values_ProductYearDistrictCrop] FOREIGN KEY([Oid])
REFERENCES [dbo].[ProductYearDistrictCrop] ([Oid])
GO

ALTER TABLE [dbo].[ProductYearDistrictCropOption1Values] CHECK CONSTRAINT [FK_ProductYearDistrictCropOption1Values_ProductYearDistrictCrop]

然後,您將使用類似的東西來引用所有內容

DECLARE @Option1Default Decimal(10,5), --Or Whatever the appropriate datatype is
@Option2Default Decimal(10,5),
@Option3Default Decimal(10,5),
@Option4Default Decimal(10,5),
@Option5Default Decimal(10,5),
@Option6Default Decimal(10,5),
@Option7Default Decimal(10,5),
@Option8Default Decimal(10,5),
@Option9Default Decimal(10,5),
@Option10Default Decimal(10,5),
@Option11Default Decimal(10,5),
@Option12Default Decimal(10,5),
@Option13Default Decimal(10,5),
@Option14Default Decimal(10,5),
@Option15Default Decimal(10,5),
@Option16Default Decimal(10,5)

SET @Option1Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 1')
SET @Option2Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 2')
SET @Option3Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 3')
SET @Option4Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 4')
SET @Option5Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 5')
SET @Option6Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 6')
SET @Option7Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 7')
SET @Option8Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 8')
SET @Option9Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 9')
SET @Option10Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 10')
SET @Option11Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 11')
SET @Option12Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 12')
SET @Option13Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 13')
SET @Option14Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 14')
SET @Option15Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 15')
SET @Option16Default = (SELECT SystemDefaultValue FROM Options WHERE [Description] = 'Option 16')

SELECT PYDC.Oid,
CASE
   WHEN PYDCOC1.[Value] IS NULL THEN @Option1Default
   ELSE PYDCOC1.[Value]
   END AS Option1Value,
CASE
   WHEN PYDCOC2.[Value] IS NULL THEN @Option2Default
   ELSE PYDCOC2.[Value]
   END AS Option2Value,
CASE
   WHEN PYDCOC3.[Value] IS NULL THEN @Option3Default
   ELSE PYDCOC3.[Value]
   END AS Option3Value,
CASE
   WHEN PYDCOC4.[Value] IS NULL THEN @Option4Default
   ELSE PYDCOC4.[Value]
   END AS Option4Value,
CASE
   WHEN PYDCOC5.[Value] IS NULL THEN @Option5Default
   ELSE PYDCOC5.[Value]
   END AS Option5Value,
CASE
   WHEN PYDCOC6.[Value] IS NULL THEN @Option6Default
   ELSE PYDCOC6.[Value]
   END AS Option6Value,
CASE
   WHEN PYDCOC7.[Value] IS NULL THEN @Option7Default
   ELSE PYDCOC7.[Value]
   END AS Option7Value,
CASE
   WHEN PYDCOC8.[Value] IS NULL THEN @Option8Default
   ELSE PYDCOC8.[Value]
   END AS Option8Value,
CASE
   WHEN PYDCOC9.[Value] IS NULL THEN @Option9Default
   ELSE PYDCOC9.[Value]
   END AS Option9Value,
CASE
   WHEN PYDCOC10.[Value] IS NULL THEN @Option10Default
   ELSE PYDCOC10.[Value]
   END AS Option10Value,
CASE
   WHEN PYDCOC11.[Value] IS NULL THEN @Option11Default
   ELSE PYDCOC11.[Value]
   END AS Option11Value,
CASE
   WHEN PYDCOC12.[Value] IS NULL THEN @Option12Default
   ELSE PYDCOC12.[Value]
   END AS Option12Value,
CASE
   WHEN PYDCOC13.[Value] IS NULL THEN @Option13Default
   ELSE PYDCOC13.[Value]
   END AS Option13Value,
CASE
   WHEN PYDCOC14.[Value] IS NULL THEN @Option14Default
   ELSE PYDCOC14.[Value]
   END AS Option14Value,
CASE
   WHEN PYDCOC15.[Value] IS NULL THEN @Option15Default
   ELSE PYDCOC15.[Value]
   END AS Option15Value,
CASE
   WHEN PYDCOC16.[Value] IS NULL THEN @Option16Default
   ELSE PYDCOC16.[Value]
   END AS Option16Value
FROM ProductYearDistrictCrop PYDC   
   LEFT OUTER JOIN ProductYearDistrictCropOption1Values PYDCOC1
       ON PYDC.Oid = PYDCOC1.Oid --assuming that is the FK on ProductYearDistrictCropOption1Values that references ProductYearDistrictCrop.Oid
   LEFT OUTER JOIN ProductYearDistrictCropOption2Values PYDCOC2
       ON PYDC.Oid = PYDCOC2.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption3Values PYDCOC3
       ON PYDC.Oid = PYDCOC3.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption4Values PYDCOC4
       ON PYDC.Oid = PYDCOC4.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption5Values PYDCOC5
       ON PYDC.Oid = PYDCOC5.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption6Values PYDCOC6
       ON PYDC.Oid = PYDCOC6.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption7Values PYDCOC7
       ON PYDC.Oid = PYDCOC7.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption8Values PYDCOC8
       ON PYDC.Oid = PYDCOC8.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption9Values PYDCOC9
       ON PYDC.Oid = PYDCOC9.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption10Values PYDCOC10
       ON PYDC.Oid = PYDCOC10.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption11Values PYDCOC11
       ON PYDC.Oid = PYDCOC11.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption12Values PYDCOC12
       ON PYDC.Oid = PYDCOC12.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption13Values PYDCOC13
       ON PYDC.Oid = PYDCOC13.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption14Values PYDCOC14
       ON PYDC.Oid = PYDCOC14.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption15Values PYDCOC15
       ON PYDC.Oid = PYDCOC15.Oid 
   LEFT OUTER JOIN ProductYearDistrictCropOption16Values PYDCOC16
       ON PYDC.Oid = PYDCOC16.Oid 
--WHERE Condition(s)

這個版本是:

  1. 規範化並防止您添加一堆額外的列,使其ProductYearDistrictCrop更寬
  2. 允許您僅插入與預設值ProductYearDistrictCrop.Oid不同的值。這可以讓您節省大量空間
  3. 附加選項只需要table遵循相同格式的附加選項,然後無論functions/ queries/中的程式碼更新都支持該選項stored procedures

當涉及到新選項的到來時,這可能是更難支持的選項之一。它還需要在您的初始開發中進行最多的工作。但是 aVIEW可能可以用來解決大部分問題。然而,我認為空間和性能方面它會帶來很多好處,這應該是數據庫的首要任務,特別是如果你已經在這個表的 200 萬行世界中。

引用自:https://dba.stackexchange.com/questions/241341