2012-04-23 32 views
1

內下一個間隙假設我有下表:查找值

id name base index 
0 A  2  0 
1 B  2  2 
2 C  2  4 
3 D  2  6 
4 E  2  8 
5 F  2  10 

所以,索引=鹼基* i,其中i是該行的在一個序列中的位置。

不時,一些行被刪除,例如,如果我刪除名爲C行和d:

id name base index 
0 A  2  0 
1 B  2  2 
4 E  2  8 
5 F  2  10 

新行後,最後總是被添加,所以下一行是MAX(索引)+ base = 12,但由於刪除的行導致索引列中值之間的差距在一段時間後會成爲問題。如果不是插入最後一個,我將它插入第一個可用缺口中,問題不會發生。

所以,我懷疑有沒有發現第一個可用間隙的問題和MAX(索引)一樣高效,但最有效的解決方案是什麼?也許它夠好。

如果不清楚,我需要找到第一行'a',使得索引值最接近的行大於a.index + a.base。

這意味着對於任何SQL數據庫使用ORM的應用程序,因此它必須是嚴格標準的SQL。

編輯

這纔是真正的表和實際問題簡單化,我期待僅使用基本和索引列的解決方案。涉及在其他表格中添加新列或索引的解決方案對於我的應用程序不實用。

編輯2

看來基本列正在使問題更加複雜,但這不是必需的。這個問題可以簡化爲表所示:

id name index 
0 A  0 
1 B  1 
4 E  4 
5 F  5 

在哪裏,我需要找到第一行「A」,使得與比a.index + X高指數最低的行。在這種情況下x = 1.

枚舉而不先排序或利用id不是可靠的解決方案,因爲這些可以改變。例如,如果行也是這樣的,則解決方案必須工作:

id name index 
0 A  0 
23 F  5 
45 E  4 
90 B  1 
+0

我懷疑ORM的查詢語言可以解決任何數據庫的這種問題。大多數ORM迎合共同點,如果沒有窗口函數,這個問題很難解決。 Afaik沒有ORMs抽象了窗口函數,但最能體現的窗口函數最近纔出現在主要的RDBMS中。在Postgresql上,版本8.4(2009);甚至SQL Server 2005都具有窗口功能,因爲er .. 2005,僅在SQL Server 2012上支持LAG和LEAD窗口功能;甚至是運行總計'(SUM OVER())'在SQL Server 2008上不起作用 – 2012-04-23 02:38:17

+0

如果確實總是index = base * i,那麼兩次存儲相同的信息(即數據庫沒有正常化)。您可以即時計算「指數」。 – gcbenison 2012-04-23 02:39:06

+0

@MichaelBuen即使ORM無法處理問題,我也可以使用原始SQL,但它仍然必須與ORM支持的所有數據庫兼容。 – 2012-04-23 13:50:35

回答

1

我不清楚你的問題意味着如果表中有多個「基」值。例如,「索引值最接近的行」是否必須具有相同的「基本」值?

在任何情況下,如果您使用實現函數LEAD()的SQL平臺,這可能是一個開始。您可能不得不用適當的方言重新翻譯TOP。將999999999替換爲大於index + base的最大可能值的任何值。

with LeadAdded as (
    select 
    lead(index,1,999999999) over (order by index) as nxt, 
    * 
    from yourTable 
) 
    select top (1) * 
    from LeadAdded 
    where nxt > index + base; 
    order by index 
+0

是的,它具有相同的基礎值,但是整個事情將在where子句過濾時發生。 – 2012-04-23 13:37:28

+0

即使平臺具有LEAD(),也不會爲下一個物理行工作嗎? – 2012-04-23 13:38:20

+0

佩德羅,我不知道你的意思是「下一個物理排」。在這個例子中,在我使用LEAD的地方,它會在「索引最低的行高於a.index + x」的行中提供索引值,這就是你如何描述你想要的。 OVER子句定義LEAD函數的「下一個」含義,帶TOP(1)和ORDER的外部WHERE子句選擇高於index + base的單個最低索引。 – 2012-04-23 16:09:47

0

而不是刪除該行,是否可以添加另一列以將其標記爲可用?然後,您可以從您的表中選擇標記爲「AVAILABLE」的MIN(id)與給定的基數。如果找不到,則插入。那樣你可以避免出現缺口,保留歷史記錄,並且可能會簡化?

+0

不是。這只是對問題的簡化,實際的表格要複雜得多,並且嵌套的值也必須標記爲級聯可用,這使得它實際上比我試圖避免的壓縮維護操作更復雜。 – 2012-04-23 01:51:38

0

大多數SQL方言支持窗口功能,讓你可以這樣做:

select min(id) 
from 
(
    select t.*, 
     row_number() over (order by id) as rownum 
    from t 
) 
where id <> rownum 

這將返回第一個ID,它是無序的。

我可能會建議,類似於第一個建議。刪除一行時,將該ID存儲在另一個「可用」ID表中。插入時,先看這張表。如果沒有可用的,則創建一個新的。

+0

如果我理解它是正確的,那麼它可以用於有史以來的第一次運行,但隨後在下一個ID中仍然沒有順序,因爲它沒有改變,但是下一個索引被填充。不僅如此,在這個應用程序中id會非常不穩定,因爲一行可以有base和index變爲另一個,並且完全保留這個base集。我真的需要一個只使用base和index的解決方案。 – 2012-04-23 02:11:13

0

嗯,這將不依賴於超出標準的SQL任何一個方法是保持「的index所有可能的值」單獨的表:

SELECT * FROM indices LIMIT 7; 
+------+ 
| idx | 
+------+ 
| 0 | 
| 2 | 
| 4 | 
| 6 | 
| 8 | 
| 10 | 
| 12 | 
+------+ 
7 rows in set (0.00 sec) 

然後讓我們說你的用戶表看起來是這樣的,與發生在指數= 4的第一間隙:

SELECT * FROM users; 
+------+------+------+------+ 
| id | name | base | idx | 
+------+------+------+------+ 
| 0 | A | 2 | 0 | 
| 1 | B | 2 | 2 | 
| 4 | E | 2 | 8 | 
| 5 | F | 2 | 10 | 
+------+------+------+------+ 
4 rows in set (0.00 sec) 

您可以使用索引表LEFT JOIN找到這個第一間隙:

SELECT indices.* 
FROM indices 
LEFT JOIN users 
USING(idx) 
WHERE users.idx IS NULL 
ORDER BY idx 
LIMIT 1; 

+------+ 
| idx | 
+------+ 
| 4 | 
+------+ 
1 row in set (0.00 sec) 

如果在索引表結束後出現第一個間隔,則會失敗,在這種情況下,您可以檢測到錯誤並擴展索引表。