SQL Server, işlemden önce işlemin içinde DDL'ye işleme izin veriyor mu (görünür kılıyor)?


9

PostgreSQL'de sonra bazı test verileri ile bir tablo oluşturabilir ve bir işlem olarak üzerine bir tablo yeniden yazma sonuçlanan farklı bir tipte bir sütuna migrate bunun COMMIT,

CREATE TABLE foo ( a int );
INSERT INTO foo VALUES (1),(2),(3);

Bunu takiben,

BEGIN;
  ALTER TABLE foo ADD COLUMN b varchar;
  UPDATE foo SET b = CAST(a AS varchar);
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

Ancak, Microsoft SQL Server'da aynı şey bir hata oluşturuyor gibi görünüyor. (Sütun) komutunun işlemin dışında olduğu bu çalışma db kemanını karşılaştırın ADD,

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
COMMIT;

-- txn2
BEGIN TRANSACTION;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

Buna db keman çalışması değil,

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

Ama bunun yerine hatalar

Msg 207 Level 16 State 1 Line 2
Invalid column name 'b'.

DDL ile ilgili olarak bu işlemi görünür kılmak için PostgreSQL gibi davranıyor mu?

Yanıtlar:


17

Genel olarak, hayır. SQL Server yürütülmeden önce toplu işin tamamını geçerli kapsamda derler, bu nedenle başvurulan varlıkların olması gerekir (deyim düzeyinde yeniden derlemeler daha sonra da olabilir). Ana istisna Ertelenmiş Ad Çözümlemesi'dir , ancak sütunlar için değil tablolar için geçerlidir:

Ertelenmiş ad çözümlemesi yalnızca varolmayan tablo nesnelerine başvurduğunuzda kullanılabilir. Saklanan yordamın oluşturulduğu sırada diğer tüm nesnelerin bulunması gerekir. Örneğin, saklı yordamdaki varolan bir tabloya başvurduğunuzda, o tablo için varolmayan sütunları listeleyemezsiniz.

Sık karşılaşılan geçici çözümler dinamik kodu (Joe'nun cevabında olduğu gibi ) veya DML ve DDL'yi ayrı gruplara ayırmayı içerir.

Bu özel durum için şunları da yazabilirsiniz:

BEGIN TRANSACTION;

    ALTER TABLE dbo.foo
        ALTER COLUMN a varchar(11) NOT NULL
        WITH (ONLINE = ON);

    EXECUTE sys.sp_rename
        @objname = N'dbo.foo.a',
        @newname = N'b',
        @objtype = 'COLUMN';

COMMIT TRANSACTION;

Yine bde aynı toplu iş ve kapsamda yeniden adlandırılan sütuna erişemezsiniz , ancak bu işi tamamlar.

SQL Server ile ilgili olarak, bir işlemde DDL ve DML'yi karıştırmanın harika bir fikir olmadığını söyleyen bir düşünce okulu vardır. Geçmişte, bu işlemin yanlış günlüğe kaydedilmesine ve kurtarılamaz bir veritabanına yol açtığı hatalar olmuştur. Bununla birlikte, insanlar özellikle geçici masalarda yaparlar. Bu, izlemesi oldukça zor bazı kodlarla sonuçlanabilir.


12

Bu aradığınız şey mi?

BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  EXEC sp_executesql N'UPDATE foo SET b = CAST( a AS varchar )';
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

2

Paul White'ın cevabı hakkındaki "genel olarak hayır" ifadesine göre, umarım aşağıdaki soruya doğrudan bir cevap sunar, ancak aynı zamanda böyle bir sürecin sistemik sınırlamalarını göstermeye hizmet eder ve sizi kolay yönetim ve ifşa etmeyen yöntemlerden uzak tutar riske atar.

Yapabilirsiniz DDL Eğer DML yapıyoruz aynı zaman değiştirir yapmamaya defalarca sayılabilir. İyi programlama, desteklenebilirliği korumak ve spagetti yayılma değişikliklerini önlemek için bu işlevleri ayırır.

Ve Paul'un dikkat çektiği gibi, SQL Server toplu olarak çalışır .

Şimdi, bundan şüphe duyanlar için, muhtemelen sizin örneğinizde değil, 2017 gibi bazı sürümler gerçekten işe yarayabilir! İşte kanıt: resim açıklamasını buraya girin

[TEST KODU - SQL Server'ın birçok sürümünde çalışmayabilir]

USE master
GO
CREATE TABLE foo (a VARCHAR(11) )
GO
BEGIN TRANSACTION;
    INSERT INTO dbo.foo (a)
    VALUES ('entry')
/*****
[2] Check Values
*****/
    SELECT a FROM dbo.foo
/*****
[3] Add Column
*****/
    ALTER TABLE dbo.foo
        ADD b VARCHAR(11)
/*****
[3] Insert value into this new column in the same batch
-- Again, this is just an example. Please do not do this in production
*****/
    IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        INSERT INTO dbo.foo (b)
        VALUES ('d')
COMMIT TRANSACTION;
/*****
[4] SELECT outside transaction
-- this will fail
*****/
    --IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
    --      AND name = 'b')
    --  SELECT b FROM dbo.foo
-- this will work...but a SELECT * ???
IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        SELECT * FROM dbo.foo

DROP TABLE dbo.foo

[Sonuç]

Evet, DDL ve DML'yi SQL Server'ın @AndriyM - dbfiddle gibi SQL Server'ın belirli sürümleri veya yamaları için aynı toplu işte gerçekleştirebilirsiniz , ancak tüm DML desteklenmez ve bunun her zaman böyle olacağının garantisi yoktur. Çalışırsa, bu SQL Server sürümünüzde bir sapma olabilir ve bu, yeni sürümlere eklerken veya yeni sürümlere geçerken önemli sorunlara neden olabilir.

  • Ayrıca, genel olarak tasarımınız değişiklikleri öngörmelidir. Sütunları değiştirmenin / eklemenin bir masada olabileceği endişelerini anlıyorum, ancak toplu olarak bu şekilde doğru bir şekilde tasarlayabilirsiniz.

[EKSTRA KREDİ]

EXISTS ifadesine gelince, Paul'un belirttiği gibi, kodunuzdaki bir sonraki adıma geçmeden önce kodu doğrulamak için birçok başka yol vardır.

  • EXISTS deyimi, SQL Server'ın tüm sürümlerinde çalışan kodlar oluşturmanıza yardımcı olabilir
  • Tek bir ifadede karmaşık denetimlere izin veren bir Boolean işlevidir

Hayır, bunu sütunu oluşturduğunuz toplu işte yapıyorsanız yeni sütuna ekleyemezsiniz . Daha genel olarak, yeni grup oluşturulduktan sonra aynı toplu işteki yeni sütunlara statik olarak başvuramazsınız. IF EXISTS hilesi bu durumda çalışmaz. DML'yi dinamik olarak çağırın veya başka bir toplu işte yapın.
Andriy M

@AndriyM üzgünüm, yanlış dbfiddle hakkında bir açıklama yaptı. Peki bunu Örneğinizde denediniz mi? 2017 SP1'de çalışır. Bir gif yükleyeceğim, ancak bunu sistemlerinizde test ettiniz mi?
clifton_h

i.imgur.com/fhAC7lB.png Aslında b, insert deyimindeki dalgalı çizgiye göre derlenmeyeceğini söyleyebilirsiniz . SQL Server 2014 kullanıyorum
Andriy M

@AndriyM ilginç. Bu etkinin daha önce çalıştığını görmüştüm ve bahsettiğim SQL Server 2017 gibi SQL Server'ın bazı sürümlerinde çalışıyor gibi görünüyor.
clifton_h

@AndriyM yayınlamak için yeni bir düzenleme görmek
clifton_h
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.