Bir tabloda kayıtları rasgele sipariş etme


28

Bir veritabanı kullanırken ortak bir ihtiyaç, kayıtlara sırayla erişmek. Örneğin, bir blogum varsa, blog postalarımı isteğe göre sıralayabilmek istiyorum. Bu girdilerin çoğu zaman birçok ilişkisi vardır, bu nedenle ilişkisel bir veritabanı mantıklı görünmektedir.

Ben gördük ortak çözüm bir tamsayı sütun eklemek için order:

CREATE TABLE AS your_table (id, title, sort_order)
AS VALUES
  (0, 'Lorem ipsum',   3),
  (1, 'Dolor sit',     2),
  (2, 'Amet, consect', 0),
  (3, 'Elit fusce',    1);

Sonra sıraları orderdoğru sırayla almak için sıralayabiliriz.

Ancak, bu sakar görünüyor:

  • Kayıt 0'ı başa taşımak istersem, her kaydı yeniden sıralamalıyım.
  • Eğer ortasına yeni bir kayıt eklemek istersem, ondan sonraki tüm kayıtları yeniden sıralamalıyım.
  • Bir kaydı kaldırmak istersem, bundan sonraki her kaydı yeniden sıralamalıyım.

Gibi bir durum hayal etmek kolaydır:

  • İki kayıt aynı order
  • orderKayıtlar arasında boşluklar var.

Bunlar bir takım sebeplerden dolayı oldukça kolay olabilir.

Joomla gibi uygulamaların kabul ettiği yaklaşım budur:

Joomla'nın siparişe yaklaşımı örneği

Buradaki arayüzün kötü olduğunu ve insanların doğrudan numaraları düzenlemek yerine okları veya sürükle bırak yöntemini kullanmaları gerektiğini ve muhtemelen haklı olacağını iddia edebilirsiniz. Ama perde arkasında, aynı şey oluyor.

Bazı insanlar, 2. ve 3. sıradaki kayıtlar arasına bir kayıt eklemek için "2.5" özelliğini kullanabilmeniz için siparişi saklamak için bir ondalık kullanmayı önerdiler. garip ondalık sayılar (nerede duruyorsun? 2.75? 2.875? 2.8125?)

Bir masada siparişi saklamanın daha iyi bir yolu var mı?


5
Sadece bilmen için. . . İlişkisel "Böyle sistemler denir nedeni '' terimi olmasıdır ilişki sadece matematiksel bir terim için temelde masaya ..." - Veri Tabanı Sistemlerine Giriş , CJ Date, 7. baskı. p 25
Mike Sherrill 'Kedi Hatırlama'


@ MikeSherrill'CatRecall 'anlamadığım, eski ordersve ddl ile soruyu düzelttim .
Evan Carroll

Yanıtlar:


17

Kayıt 0'ı başa taşımak istersem, her kaydı yeniden sıralamalıyım.

Hayır, daha basit bir yol var.

update your_table
set order = -1 
where id = 0;

Eğer ortasına yeni bir kayıt eklemek istersem, ondan sonraki tüm kayıtları yeniden sıralamalıyım.

"Arasında" değerleri destekleyen bir veri türü kullanmazsanız bu doğru. Değişken ve sayısal türler, bir değeri 2.5 olarak güncellemenizi sağlar. Fakat varchar (n) da çalışır. ('A', 'b', 'c' düşünün; daha sonra 'ba', 'bb', 'bc' düşünün.)

Bir kaydı kaldırmak istersem, bundan sonraki her kaydı yeniden sıralamalıyım.

Hayır, daha basit bir yol var. Sadece satırı sil. Kalan satırlar yine de doğru şekilde sıralanacaktır.

Gibi bir durum hayal etmek kolaydır:

İki kayıt aynı sırada

Eşsiz bir kısıtlama bunu önleyebilir.

Kayıtlar arasında sırada boşluklar var

Boşlukların, dbms'nin bir sütundaki değerleri nasıl sıraladığı üzerinde etkisi yoktur.

Bazı insanlar, 2. ve 3. sıradaki kayıtlar arasına bir kayıt eklemek için "2.5" özelliğini kullanabilmeniz için siparişi saklamak için bir ondalık kullanmayı önerdiler. garip ondalık sayılar (nerede duruyorsun? 2.75? 2.875? 2.8125?)

Eğer kadar bitmiyor var etmek. Dbms yok ondalık noktasından sonra 2, 7 veya 15 yerler var değerleri sıralama sorunu.

Bence asıl probleminiz, değerleri tamsayı olarak sıralanmış düzende görmek istemeniz . Bunu yapabilirsin.

create table your_table (
  id int primary key, 
  title varchar(13), 
  sort_order float
);

insert into your_table values
(0, 'Lorem ipsum', 2.0),
(1, 'Dolor sit', 1.5),
(2, 'Amet, consect', 0.0),
(3, 'Elit fusce', 1.0);

-- This windowing function will "transform" the floats into sorted integers.
select id, title,
       row_number() over (order by sort_order)
from your_table

with cte as (select *,row_number() over (order by sort_order desc) as row from test) update cte set sort_order=row;
Düzgünlük

İşte size ilave bir ipucu: Gerçekten mükemmel olmasını istiyorsanız, daha fazla satır taşııp atmadığınızı kontrol etmelisiniz, sonra dokunulmamış olarak kalmasını isteyin. Eğer öyleyse, o zaman daha az sayısız güncelleme - "el değmemiş" - olanlar; D
Ruben Boeck

7

O çok basit. Bir "kardinalite deliği" yapısına sahip olmanız gerekir:

2 sütuna sahip olmanız gerekir:

  1. pk = 32bit integer
  2. sipariş = 64bit bigint( değil double )

Ekle / güncelleme

  1. İlk yeni kaydı eklerken, order = round(max_bigint / 2) .
  2. Tablonun başına eklerken, order = round("order of first record" / 2)
  3. Masanın sonuna yerleştirirken, order = round("max_bigint - order of last record" / 2) 4'ü ayarlayın ) Ortaya yerleştirirken,order = round("order of record before - order of record after" / 2)

Bu yöntem çok büyük bir kardinaliteye sahiptir. Eğer bir sınırlama hatası varsa veya kardinalitenizin düşük olduğunu düşünüyorsanız, sipariş sütununu yeniden oluşturabilirsiniz (normalleştir).

Normalleştirme ile maksimum durumda (bu yapı ile) 32 bit "kardinalite deliği" olabilir.

Kayan nokta türlerini kullanmamayı unutmayın - sipariş kesin bir değer olmalıdır!


4

Genel olarak, sicil kayıtları, ünvanı, kimliği veya bu özel duruma uygun olan herhangi bir bilgiye göre sipariş yapılır.

Özel bir sipariş vermeniz gerekiyorsa, bir tamsayı sütunu kullanmak göründüğü kadar kötü değildir. Örneğin, bir kaydın 5. sıraya girmesi için yer açmak için, şöyle bir şey yapabilirsiniz:

update table_1 set place = place + 1 where place > 5.

Umarım sütunu olarak ilan edebilir uniqueve belki de yeniden düzenlemeleri "atomik" yapmak için bir prosedür uygulayabilirsiniz. Detaylar sisteme bağlı ama genel fikir bu.


4

… Tartışmalı bir şekilde daha da dağınıktır, çünkü garip onluklarla sonuçlanabilir (nerede duruyorsun? 2.75? 2.875? 2.8125?)

Kimin umrunda? Bu sayılar yalnızca bilgisayarın başa çıkması için oradadır, bu nedenle kaç kesirli basamağa sahip oldukları veya bize ne kadar çirkin göründükleri önemli değildir.

Ondalık değerlerin kullanılması, F öğesini J ve K öğeleri arasında hareket ettirmek için tek yapmanız gereken J ve K için emir değerlerini seçmek ve ardından F'yi güncellemek ve F'yi güncellemektir. kilitlenmeleri).

Çıktıdaki kesirlerden ziyade tamsayılar görmek istiyorsanız, istemci uygulamasındaki tamsayıları hesaplayın veya ROW_NUMBER () veya RANK () işlevlerini kullanın (RDBMS'niz bunları içeriyorsa).


1

Kendi projemde ondalık sayı çözümüne benzer bir çözüm denemeyi planlıyorum ancak bunun yerine bayt dizileri kullanıyorum:

def pad(x, x_len, length):
    if x_len >= length:
        return x
    else:
        for _ in range(length - x_len):
            x += b"\x00"
        return x

def order_index(_from, _to, count, length=None):
    assert _from != _to
    assert _from < _to

    if not length:
        from_len = len(_from)
        to_len = len(_to)
        length = max(from_len, to_len)

        _from = pad(_from, from_len, length)
        _to = pad(_to, to_len, length)

    from_int = int.from_bytes(_from, "big")
    to_int = int.from_bytes(_to, "big")
    inc = (to_int - from_int)//(count + 1)
    if not inc:
        length += 1
        _from += b"\x00"
        _to += b"\x00"
        return order_index(_from, _to, count, length)

    return (int.to_bytes(from_int + ((x+1)*inc), length, "big") for x in range(count))
>>> index = order_index(b"A", b"Z", 24)
>>> [x for x in index]
[b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y']
>>> 
>>> index = order_index(b"A", b"Z", 25)
>>> [x for x in index]
[b'A\xf6', b'B\xec', b'C\xe2', b'D\xd8', b'E\xce', b'F\xc4', b'G\xba', b'H\xb0', b'I\xa6', b'J\x9c', b'K\x92', b'L\x88', b'M~', b'Nt', b'Oj', b'P`', b'QV', b'RL', b'SB', b'T8', b'U.', b'V$', b'W\x1a', b'X\x10', b'Y\x06']

Buradaki fikir hiçbir zaman olası değerler arasında asla tükenmeyeceğinizdir, çünkü b"\x00"daha fazla değere ihtiyaç duyuyorsanız sadece kayıtlara eklersiniz. ( intPython 3'te sınırlandırılmamışsa, aksi halde karşılaştırmak için sonunda bir bayt dilimi seçmek zorunda kalacaksınız, varsayımın, iki bitişik değer arasında, farkların sonuna doğru toplanacağı varsayılmıştır.)

Örneğin, iki kayıtları demek, b"\x00"ve b"\x01", ve bir rekor aralarında gitmek istiyorum. Orada arasında herhangi mevcut değerleri değildir 0x00ve 0x01, ekleyin.Yani böylece b"\x00"hem ve şimdi yeni değerler eklemek için kullanabileceğiniz aralarında değerlerin bir grup var.

>>> records = [b"\x00", b"\x01", b"\x02"]
>>> values = [x for x in order_index(records[0], records[1], 3)]
>>> records = records + values
>>> records.sort()
>>> records
[b'\x00', b'\x00@', b'\x00\x80', b'\x00\xc0', b'\x01', b'\x02']

Veritabanını kolayca sıralayabilirsiniz çünkü her şey sözlükbilim sırasına girer. Bir kaydı silerseniz, hala sırayla. Projemde, yaptığım b"\x00"ve b"\xff"olarak FIRSTve LASTkayıtlar olsa da, "dan" sanal olarak bu kullanmak için ve değerler "den" için başa getirebilir / ekleme yeni kayıtlar:

>>> records = []
>>> value = next(order_index(FIRST, LAST, 1))
>>> value
b'\x7f'
>>> records.append(value)
>>> value = next(order_index(records[0], LAST, 1))
>>> value
b'\xbf'
>>> records.append(value)
>>> records.sort()
>>> records
[b'\x7f', b'\xbf']
>>> value = next(order_index(FIRST, records[0], 1))
>>> value
b'?'
>>> records.append(value)
>>> records.sort()
>>> records
[b'?', b'\x7f', b'\xbf']

0

Bu cevabı çok daha iyi buldum . Tamamen alıntı:

Veritabanları belirli şeyler için optimize edilmiştir. Çok sayıda satırı hızlıca güncellemek bunlardan biridir. Bu, özellikle veritabanı çalışmalarını yapmasına izin verdiğinizde doğru olur.

Düşünmek:

order song
1     Happy Birthday
2     Beat It
3     Never Gonna Give You Up
4     Safety Dance
5     Imperial March

Ve Beat Itsonuna kadar gitmek istersen, iki sorum olacaktı:

update table 
  set order = order - 1
  where order >= 2 and order <= 5;

update table
  set order = 5
  where song = 'Beat It'

Ve bu kadar. Bu, çok büyük sayılarla çok iyi ölçeklenir. Veritabanınızdaki varsayımsal bir çalma listesine birkaç bin şarkı koymayı deneyin ve bir şarkının bir konumdan diğerine taşınmasının ne kadar sürdüğünü görün. Bunlar çok standart hale gelmiş formlara sahip olduğundan:

update table 
  set order = order - 1
  where order >= ? and order <= ?;

update table
  set order = ?
  where song = ?

Verimli olarak yeniden kullanabileceğiniz iki hazır ifade var.

Bu, bazı önemli avantajlar sağlar - masanın sırası, aklınıza gelebilecek bir şeydir. Üçüncü şarkının birorder her zaman 3 tane var. Bunu garanti etmenin tek yolu, sırayla ardışık tamsayıları kullanmaktır. Sahte bağlantılı listeler veya ondalık sayılar veya boşluklu tamsayılar kullanmak bu özelliği garanti etmenize izin vermez; Bu durumlarda, nth şarkıyı elde etmenin tek yolu tüm tabloyu sıralamak ve nth kaydını almaktır.

Ve gerçekten, bu düşündüğünden çok daha kolay. Ne yapmak istediğinizi anlamak, iki güncelleme ifadesi oluşturmak ve diğer kişilerin bu iki güncelleme ifadesine bakıp ne olduğunu anlamak kolaydır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.