2011-06-29 44 views
0

我有一個名爲datarecords的表,其中包含7個固定列,這些列在選擇查詢中始終是必需的。用戶可以添加他們想要的任意多個自定義列。我將這些信息存儲在名爲datacolumn的表中,並將這些值存儲在名爲datavalue的另一個表中。在sql查詢中添加N個動態列

現在我想創建一個查詢,從datarecord帶來7個固定列,然後添加自定義列並從這些表中獲取數據值,因爲每個數據記錄在數據值表中都有對應的值。

+1

一些RDBMS有'PIVOT'。其他人沒有。構建動態SQL在各個平臺上也不同。 –

+0

我正在使用sql server 2005出於這個原因。並且可以有n個動態列。 – Ahsan

+2

您期望的輸出示例將幫助 –

回答

2

您可以嘗試PIVOT將行中的自定義屬性轉換爲列,但是您會發現,即使支持Microsoft SQL Server中的PIVOT,您在編寫查詢之前也需要知道屬性,查詢代碼需要指定所有的屬性。 SQL中沒有辦法要求所有的自定義屬性根據需要神奇地填充儘可能多的列。

您只能通過逐行讀取自定義屬性來檢索任意數量的自定義屬性,因爲它們存儲在數據庫中。然後編寫應用程序代碼來遍歷結果。如果你願意,你可以編寫一個類將自定義屬性的多行映射到應用程序中的一個對象的字段中。

使用SQL查詢非關係數據很尷尬和不雅。這是因爲SQL旨在假定每個相同類型的邏輯實體具有固定數量的列,並且在編寫查詢之前知道列。如果您的實體具有可變屬性,則根據定義,它不能存儲爲關係。

許多人試圖使用您正在使用的設計來擴展它,但他們發現很難管理,並且不能很好地擴展。這種設計通常被稱爲Entity-Attribute-Value模型,或鍵值對。有關EAV設計陷阱的更多詳細信息,請參閱我的書SQL Antipatterns

如果您需要支持自定義屬性,這裏有幾個選擇:

  • 商店所有的自定義屬性一起在BLOB,與一些內部結構來分隔字段名和值(Serialized LOB)。您可以選擇創建倒排索引以幫助您查找給定字段具有給定值的行(請參見How FriendFeed Uses MySQL)。

  • 對動態數據使用面向文檔的數據庫,如MongoDBSolr

  • 當用戶需要自定義屬性時,使用ALTER TABLE將常規列添加到表中。這意味着您需要爲所有用戶強制執行相同的一組自定義屬性,或者存儲所有用戶的自定義屬性,並希望表格不會太寬(Single Table Inheritance),或者爲每個用戶創建一個單獨的表格列(Concrete Table Inheritance)或僅用於自定義列(Class Table Inheritance)。

+0

我認爲我的場景在許多應用程序中很常見,允許用戶在其中添加儘可能多的列,因此我們需要stroe列在一個表中的數據和這些列的數據在另一個表中的數據列和id的id或記錄這些數據需要去... – Ahsan

+0

是的,我同意這是常見的。這只是非關係型的,因此SQL(一種旨在支持關係模型的語言)不能很好地支持變量列應該不足爲奇。 –

+0

任何人通過sql查詢得到這個問題的任何想法... – Ahsan

1

編輯:請參閱注意底部的更多細節。

我面臨同樣的問題,我發現一個解決方案很慢。也許別人有加快我的研究結果的解決方案。在我的代碼中,我有一個包含三列的表格:Col1,Col2,Col3。 Col1是我的記錄ID。 Col2是我的動態列的名稱。 Col3是該列的值。所以,如果我想表示與ID 1,兩列2和3,和值在那些列的記錄:4和5,我將有以下:

Col1, Col2, Col3 
1, 2, 4 
1, 3, 5 

然後我們樞轉超過塔2,並選擇由於col2和col3組合是唯一的,MAX(或MIN或AVG,無關緊要)col3在主元中。爲了完成可變數量列的數據透視表,我們使用動態SQL生成來生成我們的SQL。這適用於小輸入數據(我相信動態SQL的FROM子句內的派生表)。一旦數據集變大,平均功能開始需要很長時間才能執行。很長一段時間。它看起來像大約1000行開始,所以也許有一個提示或其他方法可以縮短。

作爲一個說明,由於col2的和COL3圖1的值:1,我也嘗試動態生成SELECT語句如下所示:

SELECT Col1, 
    CASE WHEN Col2 = '4' THEN Col3 END [4], 
    CASE WHEN Col2 = '5' THEN Col3 END [5], 
    CASE WHEN Col2 = '6' THEN Col3 END [6], -- ... these were dyanmically generated 
FROM #example 
GROUP BY Col1 

這只是我的數據集一樣慢。你的里程可能會有所不同。這裏是一個完整的例子,說明如何爲SQL Server編寫這個工程(2005+應該運行這個)。

--DROP TABLE #example 
CREATE TABLE #example 
(
    Col1 INT, 
    Col2 INT, 
    Col3 INT 
) 

INSERT INTO #example VALUES (2,4,10) 
INSERT INTO #example VALUES (2,5,20) 
INSERT INTO #example VALUES (2,6,30) 
INSERT INTO #example VALUES (2,7,40) 
INSERT INTO #example VALUES (2,8,50) 
INSERT INTO #example VALUES (3,4,11) 
INSERT INTO #example VALUES (3,5,22) 
INSERT INTO #example VALUES (3,6,33) 
INSERT INTO #example VALUES (3,7,44) 
INSERT INTO #example VALUES (3,8,55) 

DECLARE @columns VARCHAR(100) 
SET @columns = '' 

SELECT @columns = @columns + '[' + CAST(Col2 AS VARCHAR(10)) + '],' 
FROM (SELECT DISTINCT Col2 FROM #Example) a 

SELECT @columns = SUBSTRING(@columns, 0, LEN(@columns)) 

DECLARE @dsql NVARCHAR(MAX) 

SET @dsql = ' 
select Col1, ' + @columns + ' 
from 
    (select Col1, Col2, Col3 FROM #example e) a 
PIVOT 
(
    MAX(Col3) 
    FOR Col2 IN (' + @columns + ') 
) p' 

print @dsql 
EXEC sp_executesql @dsql 

編輯:由於獨特的情況,我做這其中,我設法讓我的加速使用兩個表(一個與實體和另一個與屬性值對),並創建一個包含所有列(ID,屬性,值)的屬性值對上的聚簇索引。如果您需要快速插入,大量列,許多數據行等,我建議您以另一種方式解決這種方法。我對數據的大小和增長率有一些已知的確定性,myy解決方案適合我的範圍。

還有很多其他的解決方案更適合解決這個問題。例如,如果您需要快速插入和單個記錄讀取(或慢速讀取無關緊要),則應考慮將XML字符串打包到字段中,並在數據庫使用者中進行序列化/反序列化。如果您需要超快速寫入,超快速讀取和數據列很少添加,那麼您可以考慮更改表格。在大多數實踐中這是一個不好的解決方案,但可能適合一些問題。如果您的列有足夠頻繁的更改,但是您還需要快速讀取和寫入不是問題,那麼我的解決方案可能適用於某些數據集大小。

+0

另外,這裏是爲什麼你不應該這樣做:http://rodgersnotes.wordpress.com/2010/09/21/muck-massively-unified -code琴鍵通用的三表數據模型/ –