2017-05-31 38 views
0

我有一段代碼,我想變成一個函數。代碼的目的是將某些記錄按變量標準分組在一起,並使用STUFF()創建分組連接。我希望能夠切換髮生組的參數(因此也是STUFF的參數)。SQL Server:通過基於參數和STUFF使用條件組()

但是,以下給出的錯誤是選擇列表中的可選參數(例如下例中的OwnerName)無效,因爲它們不包含在聚合函數或GROUP BY子句中。

考慮像下面的一個簡單的例子(現實生活中的版本有很多的參數,因此爲什麼我希望能夠得到這些都爲一個查詢):

SELECT CarMake, CarModel, CASE WHEN @FlagOwnerName = 1 THEN OwnerName ELSE NULL END AS [OwnerName], SUM(CarValue), 
LicenseIDs = STUFF((SELECT ',' + CONVERT(VARCHAR(20),Cars2.LicenseID) AS [text()] 
    FROM DB.dbo.Cars Cars2 
    WHERE Cars2.CarMake = Cars1.CarMake 
     AND Cars2.CarModel = Cars1.CarModel 
     AND (@FlagOwnerName = 0 OR Cars2.OwnerName = Cars1.OwnerName) 
    FOR XML PATH('')), 1, 1, '') 
FROM DB.dbo.Cars Cars1 
GROUP BY CarMake, 
    CarModel, 
    CASE WHEN @FlagOwnerName = 1 THEN OwnerName ELSE NULL END 

編輯:如果我改變了下面的內容,那麼它似乎會返回正確的連接,除非它是NULL,那麼連接本身就是NULL。此外,如果我嘗試將值更改爲ISNULL(Cars1.OwnerName,'Placeholder')或與COALESCE類似,它會給我出現相同的錯誤(在上面的select語句中無效)。

AND (@FlagOwnerName = 0 OR Cars2.OwnerName = Cars1.OwnerName) 

AND CASE WHEN @FlagOwnerName = 1 THEN Cars1.OwnerName = Cars2.OwnerName 
+0

你的意思是你想用CREATE FUNCTION語句創建一個實際的SQL Server函數嗎?或者你只是想與你的SELECT語句內聯?這也有助於瞭解您正在使用的SQL Server版本。 – Nathan

+0

不好意思,這將成爲重複調用的存儲過程的一部分。數據庫在SQL Server 2008上。 – am109

+0

我想你可能需要更多地解釋你正在嘗試做什麼 - 你對「LicenseIDs」有什麼期望?在XML上使用STUFF函數的目的是什麼?你是否期望Cars1與Cars2成爲一對多的關係? – Nathan

回答

0

根據您的意見,我不認爲使用STUFF與FOR XML如果最好的方式來處理這一點。通常,將多行連接成單個字符串的最佳方法是使用遞歸公用表表達式(CTE)。

有一些使用CTE(和一些替代方法)here的例子。

我已經調整了其中一個CTE選項來執行類似於您所描述的操作。

首先,我已經建立了一個簡單的表格,類似於你所描述的數據:然後我用一個CTE是建立一個汽車牌照和值附加的數據集

create table #cars (CarMake varchar(50), CarModel varchar(50), CarValue INT, OwnerName varchar(50), LicenseID varchar(50)); 

insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 12000, 'Steve','ABC123'); 
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 12000, 'Bob','HED999'); 
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 19000, 'Helen','WKS444'); 
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Ford','Mustang',30000, 'Amy','JKJL88'); 
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Ford','Mustang',30000, 'Billy-Bob','EZ1111'); 
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Aston Martin','Vantage',90000, 'Mike','HY7733'); 

/按品牌/型號彙總。可變@FlagOwnerName控制是否從CTE或這些值從源表的基本值在最終SELECT語句中使用:

DECLARE @FlagOwnerName bit = 1; 

WITH cte (CarMake, CarModel, CarValueTotal, Car_Val, LicenseList, License_ID, length_) 
AS 
( 
    SELECT 
     CarMake, CarModel, 0, 0, CAST('' AS VARCHAR(8000)), CAST('' AS VARCHAR(8000)), 0 
    FROM #cars 
    GROUP BY CarMake, CarModel 
    UNION ALL 
    SELECT c.CarMake, c.CarModel, cte.CarValueTotal + c.CarValue, c.CarValue, 
      CAST(cte.LicenseList + CASE WHEN length_ = 0 THEN '' ELSE ', ' END + c.LicenseID AS VARCHAR(8000)), 
      CAST(LicenseID AS VARCHAR(8000)), 
      length_ + 1 
    FROM cte 
    INNER JOIN #cars c ON cte.CarMake = c.CarMake AND cte.CarModel = c.CarModel 
    WHERE c.LicenseID > cte.License_ID 
) 
SELECT 
    cars.CarMake, 
    cars.CarModel, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.OwnerName ELSE 'ALL' END as OwnerName, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.CarValue ELSE totals.CarValueTotal END as CarValue, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.LicenseID ELSE totals.LicenseList END as LicenseID 
FROM #cars cars 
INNER JOIN 
(
    SELECT CarMake, CarModel, LicenseList, CarValueTotal 
    FROM ( 
      SELECT CarMake, CarModel, LicenseList, CarValueTotal, 
      RANK() OVER (PARTITION BY CarMake, CarModel ORDER BY length_ DESC) 
      FROM CTE 
     ) D (CarMake, CarModel, LicenseList, CarValueTotal, rank) 
    WHERE rank = 1 
) totals ON cars.CarMake = totals.CarMake AND cars.CarModel = totals.CarModel 
GROUP BY 
    cars.CarMake, 
    cars.CarModel, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.OwnerName ELSE 'ALL' END, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.CarValue ELSE totals.CarValueTotal END, 
    CASE WHEN @FlagOwnerName = 1 THEN cars.LicenseID ELSE totals.LicenseList END 

所以當@FlagOwnerName = 1,我們得到:

CarMake   CarModel OwnerName CarValue LicenseID 
Aston Martin Vantage  Mike  90000  HY7733 
Ford   Mustang  Amy   30000  JKJL88 
Ford   Mustang  Billy-Bob 30000  EZ1111 
Toyota   Camry  Bob   12000  HED999 
Toyota   Camry  Helen  19000  WKS444 
Toyota   Camry  Steve  12000  ABC123 

而當@FlagOwnerName = 0,我們得到:

CarMake   CarModel OwnerName CarValue LicenseID 
Aston Martin Vantage  ALL   90000  HY7733 
Ford   Mustang  ALL   60000  EZ1111, JKJL88 
Toyota   Camry  ALL   43000  ABC123, HED999, WKS444 

注意,在你的評論暗示你不想返回OWNERNAME當@FlagOwnerName = 0,而這是可能的我在存儲過程(即,根據參數執行不同的查詢)我不會推薦它。最好是返回一組一致的列,如果您在報告頂部使用報告工具,那麼您可能會在其中包含一些邏輯以根據參數值隱藏列。