2015-09-18 93 views
1

首先讓我解釋一下我的數據庫結構。MSSQL:從同一行中選擇結果

我使用MSSQL的sqlsrv驅動程序的PHP。

我有兩個數據庫表Names和AccountFieldValues。

名稱具有可變字段NameID和修正字段StringValue。
此表包含帳戶字段變量ergo的所有名稱:名字,姓氏等

AccountFieldValues具有相同的變量字段FieldNameID,FieldValue和IdentityID。
此表包含每個用戶的賬戶變量的內容。

我們獲得與IdentityID = 10和的StringValue =「名字」用戶的特定變量的內容,你可以創建一個這樣的查詢:

SELECT FieldValue FROM AccountFieldValues 
WHERE IdentityID=10 AND FieldNameID IN (SELECT NameID FROM Names WHERE StringValue='FirstName') 

或者有內部聯接

SELECT ACF.FieldValue FROM AccountFieldValues ACF 
INNER JOIN Names N ON (N.StringValue='FirstName' AND N.NameID=ACF.FieldNameID) 
WHERE ACF.IdentityID=10 

當你想要一個變量內容時,這兩種方法都能正常工作。
但現在出現了這個問題,當我想在一個查詢中獲取多個變量內容時,這變得棘手。

我可以展開兩個查詢是這樣的:

SELECT FieldValue FROM AccountFieldValues 
WHERE IdentityID=10 AND FieldNameID IN 
(SELECT NameID FROM Names WHERE StringValue='FirstName' OR StringValue='LastName') 

而像這樣的其他查詢:

SELECT ACF.FieldValue AS 'FirstName', ACF2.FieldValue AS 'LastName' FROM AccountFieldValues ACF 
INNER JOIN Names N ON (N.StringValue='FirstName' AND N.NameID=ACF.FieldNameID) 
INNER JOIN AccountFieldValues ACF2 ON (ACF2.IdentityID=ACF.IdentityID) 
INNER JOIN Names N2 ON (N2.StringValue='LastName' AND N2.NameID=ACF2.FieldNameID) 
WHERE ACF.IdentityID=10 

雖然醜陋的地獄與上法比較,這會給我結果FirstName和LastName在一個結果行中。像這樣:

FirstName|LastName 
------------------ 
Foo  |Bar 

在另一方面,第一種方法給我一個結果看起來像這樣:

FieldValue 
---------- 
Foo 
Bar 

這是不可取的,因爲我無法獲取想要的AccountFieldValues在一個每個用戶結果集

是否有一種方法可以像使用醜陋聯接一樣獲取表的結果,但是像第一種方法一樣可以更快,更容易地擴展?

+1

這是[實體屬性值](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model)設計。 –

回答

1

您正在尋找的是一種轉移數據的方式。下面是一個使用條件彙總:

SQL Fiddle

SELECT 
    FirstName = MAX(CASE WHEN n.StringValue = 'FirstName' THEN acf.FieldValue END), 
    LastName = MAX(CASE WHEN n.StringValue = 'LastName' THEN acf.FieldValue END) 
FROM AccountFieldValues acf 
INNER JOIN Names n 
    ON n.NameId = acf.FieldNameId 
WHERE acf.IdentityId = 10 
GROUP BY acf.IdentityId 

這是不是動態的。您也可以刪除JOINNames,並在您的CASE中使用acf.FieldId。就像這樣:

MAX(CASE WHEN acf.FieldId = <FIELD_ID_OF_FIRSTNAME> THEN acf.FieldValue END) 

如果你想有一個動態的方法,這裏是使用動態SQL的一種方式:

SQL Fiddle

DECLARE @sql1 NVARCHAR(MAX) = '', 
     @sql2 NVARCHAR(MAX) = '', 
     @sql3 NVARCHAR(MAX) = '' 

DECLARE @identityId INT = 10 

SELECT @sql1 = 
'SELECT 
    acf.IdentityId' + CHAR(10) 

SELECT @sql2 = @sql2 + 
' , MAX(CASE WHEN acf.FieldNameId = ' + CONVERT(VARCHAR(10), t.FieldNameId) + ' THEN acf.FieldValue END) AS ' 
    + QUOTENAME(n.StringValue) + CHAR(10) 
FROM(
    SELECT DISTINCT FieldNameId FROM AccountFieldValues WHERE IdentityId = @identityId 
)t 
INNER JOIN Names n ON t.FieldNameId = n.NameId 

SELECT @sql3 = 
'FROM AccountFieldValues acf 
INNER JOIN Names n 
    ON n.NameId = acf.FieldNameId 
WHERE acf.IdentityId = @identityId 
GROUP BY acf.IdentityId' 

DECLARE @finalSql NVARCHAR(MAX) = '' 
SET @finalSql = @sql1 + @sql2 + @sql3 

EXEC sp_executesql @finalSql, N'@identityId INT', @identityId 
+1

哇!非常感謝!我不知道你可以在一個查詢中添加多個查詢,這非常有效! –

1

我通常做這種方式:

SELECT * 
FROM dbo.Names AS N 
CROSS APPLY (
    SELECT MAX(CASE WHEN N.StringValue='FirstName' THEN AFV.FieldValue END) 
     , MAX(CASE WHEN N.StringValue='LastName' THEN AFV.FieldValue END) 
    FROM dbo.AccountFieldValues AS AFV 
    WHERE AFV.FieldNameID = N.NameID 
    ) AS AFV(FirstName, LastName); 

您可以輕鬆地在CROSS APPLY那裏添加新行。這幾乎是PIVOT,因爲Felix Pamittan說。