HELLO, I’M SERHAT AND THIS IS MY FANCY TITLE.

Rails PostgreSQL Veri Tipleri: Constraint ve Migrasyonlar

Data integrity ve referential integrity konularının epey önemli olduğu bir Rails+PostgreSQL projesinde rein GEM'ini kullanıyoruz. Constraint yönetimini oldukça kolaylaştıran bu aracı kullanırken ekip içerisinde bir takım standartlar da belirledik. İş bu doküman ekip içerisinde kullanılmak üzere yazılmış olup, benzer ihtiyaçları olanlara da faydalı olabilir düşüncesiyle paylaşılmıştır. Constraint'ler için bknz Rails ve PostgreSQL: Constraint'ler.

Boolean

  • boolean alanlar için mutlaka null_constraint eklenmelidir.

  • boolean alanlara mutlaka default bir değer tanımlanmalıdır. Örn: default: false gibi.

  • Boolean bir alanın nil olması beklenen bir durum değildir. Örneğin:

    Student.where(active: nil).count => 50
    Student.where(active: false).count => 40
    Student.where(active: true).count => 60

Bu durumda nil değerini içeren kayıtların tek tek incelenerek düzeltilmesi gerekir.

Integer

  • Integer'lar üzerinde limit: N kullanmayın.

  • Çoğunlukla integer bir değerin nil dönmesi beklenmez, 0 dönmesi beklenir. Geçerli bir sebebiniz olmadıkça null_constraint ekleyin:

    add_null_constraint :users, :articles_count
  • default bir değer (çoğunlukla 0) tanımlamak, çoğu zaman mantıklı olacaktır. default bir değer ile birlikte null_constraint ekleyin:

    t.integer :articles_count, default: 0
    add_null_constraint :users, :articles_count
  • Negatif değerleri kabul etmiyorsanız numericality_constraint ekleyin:

    add_numericality_constraint :users, :articles_count,
                                    greater_than_or_equal_to: 0
  • Olası değerleri belirli olan integer değerler için üst ve alt limitleri belirleyin:

    add_numericality_constraint :articles, :month,
                                         greater_than_or_equal_to: 1,
                                         less_than_or_equal_to: 12
    add_numericality_constraint :articles, :year,
                                         greater_than_or_equal_to: 1950,
                                         less_than_or_equal_to: 2050

Float & Decimal

  • float: Kesinliğin çok önemli olmadığı, yalnızca virgülden sonraki 3-5 basamak ile ilgilendiğiniz, ve bu rakamlar ile yoğun aritmedik işlem yapacağınız durumlarda kullanın.
  • decimal: Kesinliğin çok önemli olduğu (örneğin ödeme işlemleri), hatta performanstan da önemli olduğu durumlarda kullanın.

  • Hangi türü kullanırsanız kullanın, default bir değer tanımladıysanız null_constraint ekleyin:

    t.decimal :min_credit, precision: 5, scale: 2, default: 0
    add_null_constraint :course_types, :min_credit
  • Hangi türü kullanırsanız kullanın, negatif değerleri kabul etmiyorsanız numericality_constraint ekleyin:

    add_numericality_constraint :course_types, :min_credit,
                                           greater_than_or_equal_to: 0
  • Çoğunlukla float veya decimal bir değerin nil dönmesi beklenmez, 0 dönmesi beklenir. Geçerli bir sebebiniz olmadıkça null_constraint ekleyin:

    add_null_constraint :course_types, :min_credit
  • PostgreSQL'de float ve decimal türleri arasında çeşitli farklar bulunmakta. Öncelikle Rails'in hangi durumda ne ürettiğine bakalım:

    t.float :incentive_point
    Column          | Type              | Nullable |
    -----------------+------------------------------+
    incentive_point | double precision  |          |
    t.decimal :min_credit, precision: 5, scale: 2
    Column          | Type             | Nullable |
    -----------------+-----------------------------+
    credit          | numeric(5,2)     |          |

Yani Rails'te:

  • float -> double_precision
  • decimal -> numeric(x, y) & decimal(x, y)

  • PostgreSQL veri türüne karşılık gelmekte. Kavramlar karışmadan önce PostgreSQL'de bu veri türlerinin farklarına bakacak olursak:

    name             | size     | description | range                                                                       | in-rails |
    -------------------+----------+-------------+-----------------------------------------------------------------------------+----------+
    | decimal (p, s)   | variable | exact       | p(total digits), s(digits after decimal point), max(p)=131072, max(s)=16383 | decimal  |
    | numeric (p, s)   | variable | exact       | p(total digits), s(digits after decimal point), max(p)=131072, max(s)=16383 | decimal  |
    | double-precision | 8-bytes  | inexact     | 15 significant digits, unlimited size                                       | float    |

Buradan varılacak sonuçlar:

  • PostgreSQL'de decimal ve numeric türleri birbiriyle aynı.
  • PostgreSQL'de decimal ve numeric exact iken, float (double-precision) türü inexact.
  • numeric türü çeşitli sınırlara sahip iken double-precision türü sonsuz büyüklükte olabiliyor.

Exact?

exact özelliğinde bir veri, database'de insert edildiği şekliyle tutulurken, inexact özelliğindeki veriler insert edildikleri şekilde tutulmaz, bir dönüşüme uğrarlar. Dolayısıyla parasal işlemler gibi yuvarlama hatalarına yer olmayan durumlarda numeric tercih edilmelidir.

  • Q: Madem öyle float türünü nerede kullanabiliriz? Kim girdiği değerin tutarsız ve girdiğinden farklı bir değer olmasını ister? Hangi veri buna örnek olabilir?
  1. float'ın kullanışlı olduğu durumlardan biri büyük veriler. Örneğin 10^20 gibi bir rakamı numeric olarak tutamazken, float olarak tutabiliyoruz.
  2. float türü, decimal'a göre çok hızlı çalışmakta. Küsuratlı sayılar üzerinde yoğun çarpma, bölme, karekök alma ve benzeri işlemler gerçekleştiriyorsak, numeric türü CPU spike'larına yol açabiliyor, float bu dezavantajı ortadan kaldırıyor - ancak exact değil.

String

  • string'ler için limit: N kullanmayın. add_length_constraint kullanın.

  • text türünü kullanmayın, bunun yerine string türü kullanın ve add_length_constraint ekleyin.

  • Uzunluğundan emin olmadığınız string alanları için 255 sınırı ekleyin:

    add_length_constraint :table, :column, less_than_or_equal_to: 255 ekleyin.

  • Uzunluğundan emin olduğunuz string alanları için net limitler ekleyin:

    add_length_constraint :users, :id_number, equal_to: 11

  • 255 karakterden uzun olacağını düşündüğünüz alanlar için en fazla 65535 karakter kontrolü ekleyin:

    add_length_constraint :committee_decisions, :description, less_than_or_equal_to: 65535

  • Boş geçilmemesi gereken alanlar için add_presence_constraint kullanın:

    add_presence_constraint :countries, :name

  • Eşşiz olması gereken alanlar için add_unique_constraint kullanın:

    add_unique_constraint :users, :email

  • 50, 70 gibi herhangi bir mantığı olmayan karakter sayısı kontrolleri eklemeyin, yalnızca 255 ve 65535 kullanın.

varchar VS varchar(n) VS char VS text

  • Postgres limit tanımlamadığınız sürece tüm metin alanları için her bir satıra sınırsız (1 GB~) veri girebilmenize izin verir.
  • Postgres'te varchar (n) ve text arasında performans ve boyut yönünden herhangi bir fark bulunmamaktadır.
  • varchar(n) olası bir limit değişikliği durumunda tabloya LOCK atarak down-time yarattığı için limit tanımlamaları sütuna değil, CHECK olarak eklenmelidir.

Foreign Key

  • foreign_key türündeki sütunlar için, eğer arada optional: true bir ilişki yoksa null: false kullanın.

  • Referential integrity'yi sağlamanın kolay yollarından biri olarak referans verilen sütuna foreign_key constraint'i ekleyin.

    t.references :unit,
    null: false,
    foreign_key: true
  • Çok sık değişecekleri düşünülmediğinden ve değiştikleri zamanlarda ise durumun manuel yönetimi gerektiğinden, foreign_key constraint'leri doğrudan sütuna ekleyin, CHECK kullanmayın.

Referanslar

Date:
Categories: tech, ruby on rails, database

Share this post!


Blog Comments powered by Disqus.