2011-03-09 71 views
0

我試圖實現一個接口來翻轉訂單項的布爾狀態如何獲取聚合/分組查詢的組成行?

我被困在正確的方式來更新基礎行時,使用行集合是一個聚集。

declare @item table(
    id int not null primary key, 
    amount money not null, 
    is_paid bit not null, 
    client varchar(10) not null) 
insert into @item values 
    (1, 9.50, 0, 'Client A'), 
    (2, 11.50, 0, 'Client A'), 
    (3, 20.00, 1, 'Client B') 

查詢

select sum(amount) as total_amount, is_paid, client 
from @item 
group by is_paid, client 

結果

enter image description here

場景

現在說上面的結果是在一個網格中,如果is_paid = 0,在一個「支付」按鈕。

enter image description here

一行映射到一個或多個行的ID來進行更新點擊「支付」的結果。

我首先想到的是更新這樣的:

update @item 
set is_paid=1 
where client='Client A' 

但是分崩離析(糾正我,如果我錯了)的「客戶端A」的附加行插入時間的分鐘界面顯示以及用戶按下「付款」的時間。

問題:由於這似乎是一個相當簡單的情況,處理它的典型方法是什麼?到目前爲止,我唯一能想到的就是將聚合/分組移至應用程序邏輯。

+0

您是否擔心這樣一個事實,即附加行會無意中標記爲與其他付款一起故意標記爲已付款?但是如果額外的行能夠及時納入總量呢?用戶將如何去支付那些額外的東西來之前那些物品?一般問題是:當同一客戶有多於一件未付款項目時,如何將單個付款項目標記爲已付款? – 2011-03-09 08:48:21

+0

感謝您的評論 - 我很想掌握真正的一般問題,正如您所說的。基本上,我只是不想採取不符合用戶期望的操作 - 所以如果他們點擊付款按鈕旁邊的$ 21.00即將發生。我想我只擔心你提到的第一部分,而我不是真的,我完全理解第二部分..我將我的解決方案添加爲解決了我主要擔心的問題的答案...我會繼續嘗試雖然。 – 2011-03-09 21:33:27

回答

0

我推出我自己的答案爲這一個,我沒有看到缺點吧,但我會歡迎任何指點一下。

我喜歡的是我如何確切知道要更新哪些行。

declare @MyItem table(
    id int not null primary key, 
    amount money not null, 
    is_paid bit not null, 
    client varchar(10) not null) 
insert into @MyItem values 
    (1, 9.50, 0, 'Client A'), 
    (2, 11.50, 0, 'Client A'), 
    (3, 20.00, 1, 'Client B') 
select dbo.SumKeys(id) as [IDs], sum(amount) as total_amount, is_paid, client 
from @MyItem 
group by is_paid, client 

enter image description here

我不喜歡的是我花了半年多天得到這個代碼工作,因爲我與戰鬥(我)與對編程去古怪SQL服務器託管clr。

無論如何,我做了一個聚合,把逗號分隔的ID列表放入我的查詢中。

using System; 
using System.Collections.Generic; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 
[Serializable] 
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
    Format.UserDefined, 
    IsInvariantToDuplicates = true, 
    IsInvariantToNulls = true, 
    MaxByteSize = -1 
    )] 
public struct SumKeys : IBinarySerialize 
{ 
    private readonly static char sep = ','; 
    private SqlString result; 
    public void Init() 
    { 
     result = string.Empty; 
    } 
    public void Accumulate(SqlInt32 value) 
    { 
     if (!value.IsNull && !Contains(value)) 
      this.Add(value); 
    } 
    private void Add(SqlInt32 value) 
    { 
     this.result += Wrap(value); 
    } 
    private void Add(string value) 
    { 
     Add(Convert.ToInt32(value)); 
    } 
    private static string Wrap(SqlInt32 value) 
    { 
     return value.Value.ToString() + sep; 
    } 
    private bool Contains(SqlInt32 value) 
    { 
     return this.result.Value.Contains(Wrap(value)); 
    } 
    public void Merge(SumKeys group) 
    { 
     foreach (var value in Items(group)) 
      if (!this.Contains(value)) 
       this.Add(value); 
    } 
    private static IEnumerable<SqlInt32> Items(SumKeys group) 
    { 
     foreach (var value in group.result.Value.Split(sep)) 
     { 
      int i; 
      if (Int32.TryParse(value, out i)) 
       yield return i; 
     } 
    } 
    public SqlString Terminate() 
    { 
     return this.result.Value.TrimEnd(sep); 
    } 
    public void Read(System.IO.BinaryReader r) 
    { 
     this.result = r.ReadString(); 
    } 
    public void Write(System.IO.BinaryWriter w) 
    { 
     w.Write(this.result.Value.TrimEnd(sep)); 
    } 
} 
0

您的模式是錯誤的。您需要第三個鏈接表來存儲客戶端ID和客戶端名稱,然後在項目表中存儲客戶端ID列。然後您可以正確更新:

UPDATE @item SET is_paid = 1 
WHERE @item.Clientid = (SELECT client.clientID from Client 
WHERE Client.ClientName = 'Client A') AND @item.ID IN 
(1, 2) -- Your list of IDs checked to mark as paid in the grid 
+0

感謝您的回覆。其實,在我的真實應用程序中,我有客戶端ID。我的問題實際上是如何基於item.ID不在查詢中對行進行分組的事實來了解(1,2)部分?我有什麼意義嗎?我並不是以數據庫爲中心的proramming超級經驗,所以我現在總覺得這是一個noob。 – 2011-03-09 01:38:10

2

你可以在臨時表中創建存儲CLIENT_ID和ITEM_ID。然後通過將該表與項目表連接來選擇聚合。這樣,當is_paid = 1時,只能更新在臨時表中具有相應記錄的項目表中的記錄。例如:

// Assuming id in @item has been rename to item_id and client_id has been added to @item 

declare @active table(
    client_id int not null, 
    item_id int not null 
); 
insert into @active select client_id, item_id from @item; 

select sum(@item.amount) as total_amount, @item.is_paid, @item.client_name 
from @item inner join @active in @item.item_id = @active.item_id and @item.client_id = @active.client_id 
group by @item.is_paid, @item.client_id 
order by @item.client_name 

update @item from @item inner join @active on @item.client_id = @active.client_id and @item.item_id = @active.item_id 
set is_paid=1 
where client='Client A' 

或者,你可以添加一個create_time@item。這樣,您只能更新在特定時間之前創建的那些。例如:

select sum(amount) as total_amount, is_paid, client, max(create_time) as last_time 
from @item 
group by is_paid, client 

update @item 
set is_paid=1 
where client='Client A' and create_time <= last_time 
相關問題