mysql içinde tek bir sorguda bir güncelleştirme ve bir ekleme sorgusunun birleştirilmesine ilişkin sorgu


9

bir kullanıcı için değişikliklerin geçmişini izlemek istiyorum, böylece ne zaman onun profilini değiştirirse, eski verileri alıp geçmişte saklamak ve yeni verilerle güncellemek gerekir.

selectEski verileri almak için a , insertgeçmişe ve son olarak updateverileri değiştirmek için kullanabilirim.

tüm bu mysql saklı yordamlar, tetikleyiciler, vb kullanmadan tek bir sorguda olabilir .. kilitleri kullanarak gibi .. eğer öyleyse bana küçük bir örnek verin.


1
@savaranan: DBA'lara ve Geliştiricilere işlemleri kullanmaları ve veritabanının ACID özelliklerinden tam olarak yararlanmaları için güçlü bir hatırlatma sağladığı için bu soru +1 değerinde.
RolandoMySQLDBA

2
@savaranan: Tüm niyet ve amaçlar için Jack, SADECE makul olan cevabı verdi. Aslında, Jack Douglas ek bir adım daha attı ve GÜNCELLEME İÇİN SEÇ ... öğesini kullanarak eklenen MVCC koruması için id = 10 ile her satırda aralıklı bir kilit zorladı. Onun cevabı, Jack ve ben her zaman söylediğim noktayı daha da vurguluyor: UPDATE ve INSERT, tek bir sorgu olamaz, ancak sorunuzun önerdiği SQL davranışı için yalnızca tek bir işlem olabilir.
RolandoMySQLDBA

Yanıtlar:


13

Aynı profili aynı anda güncellemeye çalışan başka bir kullanıcıyı engelleme riski olmadan bunu yapmak için , önce satırı kilitlemenizt1 , ardından bir işlem kullanmanız gerekir (Rolando, sorunuzun yorumlarında belirtildiği gibi):

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;

Bu, id = 10 ile her satırı daha da kilitlemede mükemmeldir. Bu bir +2 olmalıdır. Verebileceğim tek şey +1!
RolandoMySQLDBA

1

Üç ifadeyi de birleştirmenin bir yolu olduğuna inanmıyorum. Buna en yakın şey gerçekten size yardımcı olmaz ve bu bir SET SELECT'dir. En iyi bahsiniz bir tetikleyicidir. Aşağıda sık sık böyle bir denetim izi (PHP ile inşa edilmiş) korumak için kullandığım bir tetikleyici örneği:

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';

-3

Bu sorgu SQL ve MySQL sunucuları üzerinde çalıştığını bulduk INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Umarım bu gelecekte de başkaları için yararlıdır.


4
Bu gerçekten bir sorgu değil. Bu aslında bir işlem olarak ele alınması gereken iki sorgudur.
RolandoMySQLDBA

@rolandomysqldba: Bu uygulama tek bir sorgu olarak tedavi nerede uygulama kodundan bir db sunucusuna göndermek zaman bu tek bir sorgu olarak iyi çalışır. neden böyle söylüyorsun?. Bunu güçlü nedenlerle çürütebilir misin ..
Saravanan

2
@saravanan: InnoDB veya herhangi bir ACID uyumlu RDBMS (Oracle, SQLServer, PostreSQL, Sybase, vb.) gözünde, bu iki SQL deyimini bir sorgu olarak adlandırmak imkansızdır. ACID uyumlu bir veritabanı olarak iki ifade gibi davranır. Varsayılan olarak, InnoDB'de otomatik taahhüt AÇIK durumdadır. İlk ifade olan INSERT tek bir işlem olarak yürütülecektir. Çok Amaçlı Eşzamanlılık Kontrolü (MVCC) verileri, t2 tablosundaki orijinal verilerin bir kopyasını satır satır temelinde tutmak için oluşturulacaktır. MySQL INSERT yürütülürken çökerse, InnoDB, t2'yi orijinal durumuna geri almak için MVCC verilerini kullanır.
RolandoMySQLDBA

1
@saravanan: INSERT'in başarılı bir şekilde çalıştığını varsayalım. INSERT'den kaynaklanan veriler işlenmiştir (otomatik devreye alma AÇIK konumdayken) ve MVCC koruma tablosu t2 atılır. UPDATE işlemini gerçekleştirdiğinizde, MVCC t1 tablosuna göre oluşturulur ve UPDATE işlemi gerçekleştirilir. MySQL UPDATE sırasında çökerse, InnoDB UPDATE'i geri almak için t1'deki MVCC verilerini kullanır. UPDATE yalnızca bir satırı değiştirse bile, kayıtları bir arada bir olasılık t1'den t 10'a t2'ye taşımak ve t1'deki id 10'u id 11'e değiştirmek değildir. Bu eşsiz senaryoyu önlemek için aşağıdakileri yapmanız gerekir ...
RolandoMySQLDBA

@savaranan: İki SQL deyimini tek bir işlem olarak ele alın. Bunu yapmanın basit yolu: BEGIN; T2 SEÇİMİ SEÇİN * t1'DEN KİMLİK = 10; GÜNCELLEME t1 SET id = 11 NEREDE id = 10; COMMIT; İki SQL deyimini tek bir işlem olarak ele almanın en güçlü nedeni, INSERT için oluşturulan MVCC'nin GÜNCELLEME sırasında varlığını sürdürmesidir. Bir MySQL çökmesi bir işlem içinde GÜNCELLEME sırasında meydana gelirse (BEGIN; ... COMMIT; block) MVCC tüm değişiklikleri tutarlı bir duruma geri döndürür. Hem INSERT hem de UPDATE tamamlanırsa, MVCC son anda atılır.
RolandoMySQLDBA
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.