與實施本爲聚合(你如果,例如,您實施了「第一非空」 SQL CLR聚合確實可以做)的問題是浪費的IO讀取每一行,當你通常只對前幾行感興趣。第一個非null即使其實現忽略更多值,聚合也不會停止。聚合也是無序的,所以你的結果將取決於查詢引擎選擇的索引的順序。
相比之下,子查詢解決方案爲每個查詢讀取最小行(因爲您只需要第一個匹配的行)並支持任何排序。它也適用於無法定義自定義聚合的數據庫平臺。
哪一個性能更好可能取決於表格中行和列的數量以及數據的稀疏程度。其他行需要爲聚合方法讀取更多行。其他列需要額外的子查詢。稀疏數據需要檢查每個子查詢中的更多行。
下面是一些結果關於各種表的大小:
Rows Cols Aggregation IO CPU Subquery IO CPU
3 3 2 0 6 0
1728 3 8 63 6 0
1728 8 12 266 16 0
的IO這裏測量的是邏輯讀取數。請注意,子查詢方法的邏輯讀取次數不會隨着表中的行數而改變。另請注意,每個附加子查詢執行的邏輯讀取可能會針對相同的數據頁(包含前幾行)。另一方面,聚合必須處理整個表並且需要一些CPU時間來完成。
這是我用於測試的代碼...SortCol上的聚集索引是必需的,因爲(在這種情況下)它將確定聚合的順序。
定義表和插入測試數據:
CREATE TABLE Table1 (Col1 int null, Col2 int null, Col3 int null, SortCol int);
CREATE CLUSTERED INDEX IX_Table1 ON Table1 (SortCol);
WITH R (i) AS
(
SELECT null
UNION ALL
SELECT 0
UNION ALL
SELECT i + 1
FROM R
WHERE i < 10
)
INSERT INTO Table1
SELECT a.i, b.i, c.i, ROW_NUMBER() OVER (ORDER BY NEWID())
FROM R a, R b, R c;
查詢表:
SET STATISTICS IO ON;
--aggregation
SELECT TOP(0) * FROM Table1 --shortcut to convert columns back to their types
UNION ALL
SELECT
dbo.FirstNonNull(Col1),
dbo.FirstNonNull(Col2),
dbo.FirstNonNull(Col3),
null
FROM Table1;
--subquery
SELECT
(SELECT TOP(1) Col1 FROM Table1 WHERE Col1 IS NOT NULL ORDER BY SortCol) AS Col1,
(SELECT TOP(1) Col2 FROM Table1 WHERE Col2 IS NOT NULL ORDER BY SortCol) AS Col2,
(SELECT TOP(1) Col3 FROM Table1 WHERE Col3 IS NOT NULL ORDER BY SortCol) AS Col3;
的CLR 「第一非空」 聚集體進行測試:
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsNullIfEmpty = true,
IsInvariantToNulls = true,
IsInvariantToDuplicates = true,
IsInvariantToOrder = false,
#if(SQL90)
MaxByteSize = 8000
#else
MaxByteSize = -1
#endif
)]
public sealed class FirstNonNull : IBinarySerialize
{
private SqlBinary Value;
public void Init()
{
Value = SqlBinary.Null;
}
public void Accumulate(SqlBinary next)
{
if (Value.IsNull && !next.IsNull)
{
Value = next;
}
}
public void Merge(FirstNonNull other)
{
Accumulate(other.Value);
}
public SqlBinary Terminate()
{
return Value;
}
#region IBinarySerialize Members
public void Read(BinaryReader r)
{
int Length = r.ReadInt32();
if (Length < 0)
{
Value = SqlBinary.Null;
}
else
{
byte[] Buffer = new byte[Length];
r.Read(Buffer, 0, Length);
Value = new SqlBinary(Buffer);
}
}
public void Write(BinaryWriter w)
{
if (Value.IsNull)
{
w.Write(-1);
}
else
{
w.Write(Value.Length);
w.Write(Value.Value);
}
}
#endregion
}
你需要的第一個非空列,或者第一個非空行? – feihtthief 2010-01-11 22:09:13
你是否只需要第一排,或者你是否需要整套? sortCol是獨一無二的嗎? – feihtthief 2010-01-11 22:27:02
@feihtthief:每列中的第一個非空值。我認爲示例輸出應該很好地顯示所需的效果。 @Mark Byers:由於我沒有一個解決方案可以一次性工作,我只能猜測它的性能,但子查詢方法還有很多不足之處。在我的實際表格中,我需要以這種方式捲起大約20行。 與子查詢方法,指標並不是特別有用。我相信單掃描方法有很多列可能會快得多。 – EvilRyry 2010-01-11 22:32:48