Neden kısıtlamalar kod yerine veritabanında uygulanır?


21

Veritabanında neden kısıtlama uygulanıyor? Kodu koya koymak daha esnek olmayacak mı?

Veritabanlarının uygulanması hakkında yeni başlayanlar kitabı okuyorum, bu yüzden bunu yeni başlayan olarak soruyorum. Diyelim ki bu varlık modeli de dahil olmak üzere bir veritabanı tasarladım:

 entity type    |   sub-types
----------------+--------------------------------------------
   Person       |   Employee, Student,       ...
   Student      |   Graduate, Undergraduate, ...
   Employee     |   Teacher,  Administrator, ...

Mevcut kısıtlamalar:

  1. Sisteme kayıtlı bir kişi yalnızca Öğrenci veya Çalışan olabilir.
  2. Kişi varlığı, her bireyin sadece tek bir benzersiz (yani yeterince iyi bir birincil anahtar) olduğunu düşündüğümüz sosyal sayının benzersizliğini gerektirir . (bkz. # 1)

Daha sonra 1 sayısını kaldırmaya karar veririz: Bir gün kolej Teacher( Employeealt türün) de Studentboş zamanlarında ders alarak olabileceğine karar verirse, binlerce, milyonlarca, milyarlarca dolarlık veritabanı tasarımını değiştirmek çok daha zordur, sadece koddaki mantığı değiştirmek yerine milyonlarca giriş: sadece bir kişinin hem öğrenci hem de çalışan olarak kaydedilmesine izin vermeyen kısım.

(Çok imkansız ama şu anda başka bir şey düşünemiyorum. Görünüşe göre mümkün).

Neden koddan ziyade veritabanı tasarımında iş kurallarını önemsiyoruz ?

# 1: 7 yıl sonra bir not, gerçek hayattan bir örnek:
Bir hükümetin, bir hata nedeniyle, yayınlanan SSN'lerin çoğaltıldığı bir hükümet gördüm: birden fazla kişi, aynı SSN. Orijinal DB tasarlayanlar kesinlikle bu teklik kısıtlamasını veritabanında uygulamayan hata yaptılar. (ve daha sonra orijinal uygulamada bir hata? Paylaşılan veritabanını kullanan ve kısıtlamayı nereye koyacağınızı, kontrol edip uygulayacağınızı kabul etmeyen birden fazla uygulama? ...).
Bu hata sistemde ve tüm sistemde yaşamaya devam edecek ve bundan sonra uzun yıllar boyunca orijinal sistemin veritabanına güvenecek. Buradaki cevapları okuduğumda, mümkün olan en iyi fiziksel dünyayı temsil etmek için veritabanındaki tüm kısıtlamaları, mümkün olduğunca çoğunu, akıllıca (kör değil) uygulamayı öğrendim.


2
Çoğunlukla iş kurallarının uygulanmasını ve bunun için en iyi yolun ne olduğunu önemsiyoruz.
ypercubeᵀᴹ

3
Varlıklarınızın esnekliği ve veritabanının genişletilebilirliği çoğunlukla normalleştirme ile tanımlandığından, aslında hangi kısıtlamaların kullanıldığına dair çok kötü bir örnek sunuyorsunuz. Bununla birlikte, kısıtlamalar, birisi doğrudan DB'yi düzenlese bile, harici bir API eklenmiş olsa bile, yeni bir uygulama geliştirilse bile, yeni bir uygulama geliştirilse bile, veritabanına girmekte olan herhangi bir bozuk veriye karşı son korumadır. Kısıtlamalar veritabanını korur, bunun yanı sıra iş mantığının da DB'ye erişmeye çalışmadan önce kendi şeylerini yapması gerekecektir.
Niels Keurentjes

3
Aslında, yüksek lisans öğrencisi olarak hem Öğrenci, Çalışan, hem de Öğretmen olarak görülüyorum. Yani örneğiniz gerçekten imkansız değil.
Winston Ewert

4
Veritabanı tasarımını hiçbir zaman uygulamanızdaki nesnelere dayandırmamalısınız. Bunu noramlly olarak kişi olarak tasarlarsınız, daha sonra kişilerin rollerini tanımlamak için ilgili bir masanız olur. O zaman sorun ortaya çıkmaz, çünkü insanların birden fazla rolü olabileceği rolleri için tablo oluşturdunuz. Yalnızca bir rol kişisine sahip olmak istiyorsanız, tabloyu peopleID'nin benzersiz olması için kısıtlarsınız. Değiştirmek istediğinizde bu kısıtlamayı kaldırın.
HLGEM

Nesne <-> İlişkisel eşleme bir sanattır.
Thorbjørn Ravn Andersen

Yanıtlar:


34

Bazı kısıtlamalar veritabanında en iyi şekilde uygulanırken bazıları uygulamada en iyi şekilde uygulanır.

Veritabanında en iyi şekilde uygulanan kısıtlamalar genellikle oradadır, çünkü bir ürünün geçerli olmasını sağlamak için yabancı bir anahtar çelişkisi gibi veri modelinin yapısı için temeldirler category_id.

Bir uygulamada uygulanan kısıtlamalar, tüm FooBar ürünlerinin mavi olması gerektiği gibi veri modeli için temel olmayabilir - ancak daha sonra birisi FooBar'ın da sarı olabileceğine karar verebilir. Bu, veritabanında olması gerekmeyen uygulama mantığıdır, ancak ayrı bir colourstablo oluşturabilirsiniz ve veritabanı, ürünün bu tablodan geçerli bir giriş başvurmasını gerektirebilir. AMA sadece kayıt olduğu karar coloursdeğeri vardır blueediyorum hala bir yere gelen dış veritabanı.

Veritabanında herhangi bir kısıtlama yoksa ve bunların uygulamada uygulanmasını gerektiriyorsa ne olacağını düşünün. Verilerle çalışmak için birden fazla uygulamanız olsaydı ne olurdu? Farklı uygulamalar kontrendikasyonları farklı şekilde uygulamaya karar verirse verileriniz nasıl görünür?

Örneğiniz, veritabanından ziyade uygulamada kısıtlamanın daha yararlı olabileceği bir durumu gösterir, ancak belki de ilk veri modelinin çok kısıtlayıcı ve esnek olmamasıyla ilgili temel bir sorun vardı?


Bu cevaba göre, <a person kuralı sadece Student alt-tipi tablosunda veya sadece Çalışan alt-tipi tablosunda> olabilir, kodda uygulanmalı ve Database'de <Student / Employee alt-türü geçerli olmalıdır kişi> kısıtlama. Haklı mıyım? (Kitabın bir örneğiydi). Teşekkürler.
hkoosha

2
@loolooyyyy: Evet, bence bu doğru. Veritabanı ilk kuralı uygularsa (bir kişi sadece öğrenci veya çalışan olabilir), o zaman tanımladığınız durum (bir çalışanın bir sınıfa kaydolmak istediği) imkansızdır çünkü: kişi hem olamaz hem de değildir muhtemelen üçüncü bir şahıstan (hükümet gibi) çıkarılan Sosyal Güvenlik Numaralarını paylaşamayacakları için ikinci bir "kişi" kaydı oluşturmak da mümkündür. Tabii ki, bu aşırı kısıtlayıcı veri modeli bazı durumlarda işe yarayabilir ...
FrustratedWithFormsDesigner

2
@loolooyyyy: Öğretmenler öğrencilerin başka tablo çağrıda etmek olabilir olalım hala orijinal veri modeli kullanmak ve bir başka yolu teachers_as_studentsbaşka alt tipi olan Studentsve atıfta yeni yabancı anahtar vardır Teachersve sistem tarafından oluşturulan bir sosyal yerine, birincil anahtar Güvenlik numarası. Bu şekilde, bir "öğrenci" aslında bir öğretmenin takma adıdır, böylece öğretmen yine de ders almak için kayıt olabilir. Tüm veri modelini görmeden bunun ne kadar iyi çalışacağından emin olmak zor.
FrustratedWithFormsDesigner

2
Bunu reddettim. Bir kısıtlamanın yalnızca uygulamada en iyi şekilde uygulandığı zaman yoktur . Bu cevabın tonu yanlış tartılmış.
Evan Carroll

3
@FrustratedWithFormsDesigner kesinlikle, aslında yabancı bir anahtar kısıtlaması için poster çocuğu. Db erişim noktasının farklı sürümlerinde / derlemelerinde üç istemciniz olduğunu varsayalım, bu ürünü kırmızı renkte göndermeyi bıraktığınızda ne yapacaksınız? Olası renk kombinasyonlarının listesini nerede saklayacaksınız ? İpucu: Senin için merkezi bir yerim var. Eğer tablo oluşturmak Ve eğer color_productsve colorFKEYs aşağıdaki çoğu IDE / şema yükleyiciler, destek -, büyük olasılıkla daha kolaylıkla ilave damla çıkışlar oluşturmak mümkün olacak.
Evan Carroll

35

Çünkü:

  1. İstediğim her veritabanında veriler aynı kısıtlamalara tabi olmak değil, sadece yeni veri bugün çalışıyor kod sürümünde kısıtlamalara tabi olmak.
  2. Programsal kısıtlamalar değil, deklaratif kısıtlamalar istiyorum.
  3. Veritabanındaki veriler genellikle bugün onunla etkileşime girmek için yazılan koddan daha fazladır. Ve bu veriler - kod değil - kurumun kıymetidir.
  4. Tüm verilerin titiz kısıtlamalara tabi olduğunu bildiğimde kodum çok daha basit hale geliyor. Artık veritabanının imkansız olduğunu garanti ettiğini bildiğim özel durumları dikkate almak zorunda değilim.

Benim için önemli olan bazı nedenler.


4
(1) ve (3) ile yarı ilişkili: uygulama kodundaki hatalar düzeltilebilir, verilerinizdeki hatalar genellikle onarılamaz.
mu çok kısa

17

Veriler muhtemelen uygulama kodundan daha uzun süre dayanacaktır. Kural, verilerin zaman içinde yararlı olması için kritikse (verilerin bütünlüğünü korumaya yardımcı olan yabancı anahtar kısıtlamaları gibi), veritabanında olması gerekir. Aksi takdirde, veritabanına çarpan yeni bir uygulamada kısıtlamayı kaybetme riskiyle karşı karşıya kalırsınız. Birden çok uygulama veritabanlarına çarpmakla kalmaz (önemli bir veri kuralı olduğunu fark etmeyebilecekler de dahil olmak üzere), aynı zamanda veri içe aktarma veya raporlama uygulamaları gibi bazıları ana veri giriş uygulamasında ayarlanan veri katmanını kullanamayabilir. Açıkçası, kısıtlamada bir hata olma şansı, uygulama kodumda çok daha yüksek.

Kişisel görüşüme göre (30 yılı aşkın bir süredir farklı amaçlar için kullanılan yüzlerce farklı veritabanıyla veri ve deneyime dayanarak), çelişkileri ait oldukları veritabanına koymayanlar sonunda zayıf verilere sahip olacaktır. Bazen kullanılamaz olma noktasına kadar kötü veriler. Bu, özellikle belirli denetim kriterlerini karşılaması gereken finansal / düzenleyici verileriniz olduğunda geçerlidir.


17

Veritabanının dışında uygulanan çoğu başvuru bütünlüğü kısıtlaması yenilebilir, bu nedenle verilerinizin her zaman garantili bütünlüğe sahip olmasını istiyorsanız, veritabanına kısıtlamalar uygulamanız gerekir. Tam durak, hepsi bu.

Genellikle, uygulama düzeyindeki kısıtlamalar, veritabanı okuma tutarlılık mekanizması aracılığıyla yenilir, ancak oturumlar işleninceye kadar diğer oturumların verilerini görüntüleyemez.

Örneğin, iki oturum aynı değeri benzersiz olması amaçlanan bir sütuna eklemeyi deneyebilir. Her ikisi de onay edebilirsiniz aynı anda değer zaten var olmadığını, her ikisi de kendi değeri eklemek ve her iki işleyebilir olabilir. Veritabanında uygulanan benzersiz bir kısıtlama bunun olmasına izin vermez.

Bu, uygulama dili tasarımcıları tarafından bilinmemektedir. Okuma bölüm 3.10 tekliği içinde Raylar Kılavuzları üzerinde Ruby: Aktif Kayıt Doğrulama ve Callbacks

Bu yardımcı, nesne kaydedilmeden hemen önce özelliğin değerinin benzersiz olduğunu doğrular. Veritabanında benzersizlik kısıtlaması oluşturmaz, bu nedenle iki farklı veritabanı bağlantısının benzersiz olmasını istediğiniz bir sütun için aynı değere sahip iki kayıt oluşturması olabilir. Bundan kaçınmak için veritabanınızda benzersiz bir dizin oluşturmanız gerekir.


16

Veritabanı tarafından uygulanan kısıtlamaların faydaları:

Basitlik - Bir kısıtlama beyan etmek, bir kısıtlama beyan etmek ve bu beyanı uygulayacak kodu yazmaktan çok daha basittir.

Doğruluk - Yazmadığınız kodda asla oluşturduğunuz bir hata olmaz. Veritabanı satıcıları, kısıtlama kodlarının doğru olduğundan emin olmak için zaman harcar, bu yüzden zorunda kalmazsınız.

Hız - Uygulamanızın asla dayalı olduğu veritabanından daha fazla dağıtımı olamaz. Veritabanı satıcıları, kısıtlama kodlarının etkili olduğundan emin olmak için zaman harcar, bu yüzden zorunda kalmazsınız. Veritabanının kendisi de bir uygulamaya ne kadar verimli olursa olsun verilere göre daha hızlı erişime sahiptir.

Yeniden kullanım - Bir platformda tek bir uygulama ile başlayabilirsiniz, ancak bu şekilde kalmayabilir. Verilere farklı bir işletim sisteminden, farklı bir donanımdan veya bir ses arabiriminden erişmeniz gerekiyorsa ne olur? Veritabanında kısıtlamalar olması nedeniyle, bu kodun yeni platform için asla yeniden yazılması ve hiçbir zaman doğruluk için hata ayıklanmaması veya hız için profillenmesi gerekmez.

Tamlık - Uygulamalar veritabanına veri girildiğinde kısıtlamaları zorunlu kılar ve eski verilerin doğru olduğunu doğrulamak veya veritabanında bulunan verileri işlemek için ek çaba gerektirir.

Uzun Ömür - Veritabanı platformunuz belirli bir uygulamadan daha uzun ömürlü olacaktır.


11

Neden kısıtlamalar sunucuya uygulanır? Çünkü kötü adamları müşterinizi kullanmaya zorlayamazsınız.

Açıklığa kavuşturmak için, yalnızca istemci uygulamanızda iş kuralı işlemesi yapıyorsanız, başka bir araç kullanan biri veritabanı sunucusuna bağlanabilir ve iş kurallarınız ve bütünlük denetimlerinizle kısıtlanmadan istediklerini yapabilir. Ağ üzerinde herhangi bir kişinin keyfi bir araç kullanmasını engellemek çok zordur.

Veritabanı sunucusunda bütünlük denetimi yaparsanız, araçtan bağımsız olarak verilere erişmek için yapılan her girişim kurallarınız tarafından kısıtlanacaktır.


10

Burada bazı harika cevaplar ve diğer düşünceleri tekrarlama riski var:

  • SSN'nin benzersiz olması gerekmez. Heck, SSN her zaman bilinmemektedir ve bazı durumlarda mevcut değildir (henüz). SSN'ler yeniden kullanılabilir ve tüm çalışanların veya öğrencilerin bir SSN'si olmayabilir. Bu, soru için çevresel bir konudur, ancak kısıtlamalarınızı nerede uygularsanız uygulasın, iş kuralları hakkında karar vermek için veri modelini ve etki alanını çok iyi anlamanız gerektiğini gösterir.
  • Şahsen, kısıtlamaların verilere olabildiğince yakın olmasını tercih ederim. Bunun en basit nedeni, herkesin veritabanındaki verileri değiştirmek için uygulama kodunu kullanmamasıdır. İş kurallarınızı uygulama düzeyinde uygularsanız ve UPDATEdoğrudan veritabanına karşı bir ifade çalıştırırsam başvurunuz geçersiz bir değişikliği nasıl önler? Uygulamadaki iş kurallarıyla ilgili bir başka sorun, özellikle herkesin güncellemeyi aynı anda alamamasının mümkün olduğu dağıtılmış uygulamalar için yeniden derleme / yeniden dağıtımın zor olabileceğidir. Ve son olarak, uygulamadaki iş kurallarını değiştirmek, yeni kuralları ihlal eden mevcut veriler hakkında kesinlikle hiçbir şey yapmaz - verilere yeni kısıtlama eklerseniz, verileri düzeltmeniz gerekir.
  • Çeşitli düzeylerde birden fazla, fazlalık denetimi doğrulayabilirsiniz. Tüm bunlar, dağıtım yöntemlerinin esnekliğine, bir değişikliğin ne kadar olası olduğuna ve bir iş kuralı değişikliğini veritabanında ve diğer katmanlarda senkronize etmenin ne kadar zor olduğuna bağlıdır. Uygulama katmanında kontrolleri tekrarlamak için zorlayıcı bir argüman, veritabanına gidiş-dönüşün sadece orada bir kısıtlamada başarısız olmasını önleyebilmenizdir (kısıtlamanın niteliğine ve mevcut verilere bağlı olup olmadığına bağlı olarak). Ama birini ya da diğerini seçmek zorunda kalsaydım, yukarıdaki nedenlerden dolayı veritabanına koyardım.

Açıkça bahsettiğiniz durumda, aniden daha önce izin verilmeyen bir şeye izin verdiğinizde, bu gerçekten bir sorun değildir - nerede olursa olsun, onu zorlayan her türlü kısıtlamayı kaldırırsınız. Aniden öğretmenlerin artık öğrenci olmasına izin verilmediği tersi durumda, daha önce kısıtlamanın nerede olduğuna bakılmaksızın, yine de temizlemek için bir grup veriye sahipsiniz.


9
  1. Veritabanı kısıtlamaları etkin bir şekilde kontrol edebilir. Koddan daha iyi.

  2. Bütünlük kısıtlamaları, veritabanının etkili yürütme planı bulmasına yardımcı olur

  3. Uygulama okunan tutarlı görünümü görür, bu nedenle benzersizliği neredeyse garanti edemez. Veritabanı aynı zamanda işlenmemiş verileri de görebilir.


8

Kısa cevap ... veri bütünlüğünü korumak için (örn. Doğruluk ve geçerlilik).

Bir istisna ...
Veritabanı, çoğu Sqlite veritabanında olduğu gibi, tek bir kullanıcının verilerini tek bir kullanıcı için saklıyorsa, kısıtlamalara gerek olmayabilir. Aslında, erişim zamanını bu kadar hızlı tutmak için genellikle ölçülemezler.

Diğer her şey için ...
Veritabanları her zaman editör ve kullanıcı diyeceğim iki efendiye hizmet eder .

Editörler çoğunlukla veritabanına veri koyar ve bir kerede bir veya az sayıda kayıt alır. Başlıca endişeleri, ilgili tüm veri parçalarına hızlı ve doğru erişim ve değişikliklerinin hızlı ve güvenilir bir şekilde depolanmasıdır.

Kullanıcılar çoğunlukla veri alır ve en çok tartışmasız doğru bilgilere hızlı erişim ile ilgilenir. Çoğunlukla, yeşil çubuklu kağıt çıktıların bu ikonik ayak kalınlığında yığınlarında oluşturulmuş olan çeşitli sayılara, toplamalara ve listelere ihtiyaç duyarlar, ancak genellikle bugün web sayfalarında yayınlanırlar.

Veritabanı geliştirme projeleri neredeyse her zaman Kullanıcılar tarafından başlatılır , ancak tasarım Editörlerin veri girişi ve bir kerede kayıt ihtiyaçları tarafından yönlendirilir . Bu nedenle, deneyimsiz geliştiriciler genellikle veritabanına kısıtlama getirmemekle birlikte doğrudan hız ihtiyacına (öncelikle geliştirme ) yanıt verirler .

Eğer sadece ve sadece bir uygulama sonsuza dek verilerde değişiklik yapmak için kullanılabilir gidiyor hayatım veritabanının ve bu uygulama bir ya da iyi koordine küçük bir birey sayısına göre geliştirilen, o zaman belki güvenmek makul veri bütünlüğünü sağlamak için uygulama.

Ancak geleceği tahmin edebileceğimizi düşündüğümüz kadar, yapamayız.

Herhangi bir veritabanı üretme çabası, onu atmak için çok değerlidir. Bir ev gibi, veritabanı da birçok kez genişletilecek, değiştirilecek ve yenilenecektir. Tamamen değiştirilse bile, tüm veriler eski iş kurallarını ve ilişkilerini korurken yeni veritabanına taşınacaktır.

Kısıtlamalar, bu kuralları ve ilişkileri, kolayca erişilebilecekleri veritabanı motorunda kısa ve bildirici bir biçimde uygular. Onlar olmadan, daha sonraki geliştiricilerin bu kuralları tersine mühendislik uygulamak için uygulama programlarından dökmeleri gerekecektir. İyi şanslar!

Bu arada, ana bilgisayar COBOL programcılarının tam olarak ilişkisel motorları ve kısıtlamaları olmadan önce bu devasa veritabanları yaratıldığı için yapması gereken şey budur. IBM'in DB2'si gibi modern bir sisteme taşınsa bile, belki de bir dizi COBOL "toplu" programında yer alan eski kuralların mantığı dönüştürmek için pratik olmayacak kadar kıvrık olabileceğinden, kısıtlamalar bazen tam olarak uygulanmamaktadır. Otomatik araçlar bunun yerine eski COBOL'u yeni ilişkisel motora arayüzleri olan yeni bir sürüme dönüştürmek için kullanılabilir ve biraz tweaking ile veri bütünlüğü korunur ... her şeyi ustaca bozan ve şirket çekilinceye kadar yeni bir uygulama yazılana kadar diyelim ki, sahip olmamaları gereken binlerce ev sahibi için haciz.


7

Diğer yorumlara ek olarak ...

Herhangi bir tablonun bir veya daha fazla uygulama veya kod yolu ile güncellenebileceği bir veritabanınız varsa / bu durumda veritabanına uygun kısıtlamalar koymak, uygulamalarınızın "aynı" kısıtlama kodunu çoğaltmayacağı anlamına gelir. Bu, bakımı basitleştirerek (bir veri modeli değişikliği varsa / olduğunda değişiklik yapılacak yer sayısını azaltarak) size fayda sağlar ve verileri güncelleyen uygulamadan bağımsız olarak kısıtlamaların tutarlı bir şekilde uygulanmasını sağlar.


5

Şahsen, kısıtlamalar oluşturmak ve değiştirmek, tetikleyicileri oluşturmaktan daha kolay olduğunu düşünüyorum, örneğin, kaynak kodunu kullanarak iş kuralınızı uygulamak için bir yol olacaktır.

Ayrıca, tetikleyicilerin genellikle PL / SQL gibi satıcıya özel dillerde yazıldığından taşınabilir olma olasılığı daha düşüktür.

Ancak kısıtlamalar ihtiyaçlarınızı karşılamıyorsa, iş kurallarınızı uygulamak için her zaman tetikleyicileri kullanabilirsiniz.


5
Ayrıca tetikler, okuma tutarlılığı sorunları nedeniyle bütünlüğü garanti etmez.
David Aldridge

3

Onlar her zaman veritabanında uygulanmalıdır ilk , çünkü

  1. Veritabanı, farklı istemciler arasında bütünlük sağlar. Farklı platformlardaki farklı istemcilerin veritabanına erişmesini sağlayabilirsiniz. Veritabanındaki kısıtlamalar, yeni bir istemci oluşturduğunuzda bütünlük sorunlarını riske atmaz. Bu, bir yeniden yazma veya ek erişim noktası durumunda kısıtlamalarınızı Q / A yapmaktan kurtarır.
  2. Veritabanının bina kısıtlamaları için bir DSL'si vardır: SQL DDL!
  3. Veritabanı, sistem kataloglarındaki bu kısıtlamalara erişim sağlar, böylece uygun bir ORM veya "şema yükleyici" bu kısıtlamaları okuyabilir ve bunları uygulamanıza getirebilir. Örneğin, veritabanınız bir varchar(5)türünüz olduğunu belirtiyorsa, belirli bir dil için şemanın türüyle eşlenen ve boyut üzerindeki kendi kısıtlamasını bir araya getiren şema yükleme ORM'sini bulma şansınız yüksektir. DBIx for Perl is one such schema loader; Varlık Çerçevesi için başka bir örnek . Bu yükleyicilerin yetenekleri değişir, ancak verebilecekleri her şey, veritabanına yolculuk yapmadan uygulamada bütünlüğü sağlamak için iyi bir başlangıçtı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.