WordPress, özel alanları paylaşılan tek bir tabloda saklar: wp_postmeta. Her değer, üç işe yarar sütundan oluşan bir satırdır: yazı kimliği, bir anahtar ve bir değer. Bu tasarım esnektir ve kataloğunuz büyüdükçe sorgulanmasının zorlaşmasının da nedenidir.
Bu bir WordPress hatası değildir. wp_postmeta bilinçli olarak genel tutulmuştur; böylece herhangi bir eklenti, veritabanı şemasını değiştirmeden herhangi bir yazıya herhangi bir değer ekleyebilir. Bu genelliğin bedeli, gerçek bir hacme ulaştığınızda ortaya çıkar: ürün kataloğu, bir listeleme dizini ya da birkaç yazıdan fazlasına ve yazı başına birkaç alandan fazlasına sahip her şey.
Bir ürün, birçok satır
On alanlı bir ürün on satır olur. Yirmi bin ürün 200.000 satır eder ve bunların her biri tabloyu, yazı meta verisi saklayan diğer tüm eklentilerle (sayfa oluşturucular, SEO eklentileri, önbellek eklentileri, temanız) paylaşır. wp_postmeta çoğu zaman bir WordPress veritabanındaki en büyük tablodur ve ürün eklemeyi bıraksanız bile büyümeye devam eder, çünkü eklentiler kendi bakım satırlarını aynı yere eklemeyi sürdürür.
Bu desene EAV denir; entity-attribute-value (varlık-öznitelik-değer) ifadesinin kısaltmasıdır. Öznitelik başına bir sütun bulunan bir tablo yerine, öznitelik değeri başına bir satır elde edersiniz ve öznitelik adı her satırda metin olarak tekrarlanır. Biçimini önceden bilmediğiniz verileri modellemenin iyi bilinen bir yoludur ve WordPress'in yazı meta verisi için bunu kullanmasının nedeni tam olarak budur: herhangi bir eklenti, veritabanı taşıması gerektirmeden yeni alanlar tanımlayabilir.
Bunun bedeli, veritabanının artık bir “alanı” sütun olarak ele alamamasıdır. Alanı, yalnızca metin bir anahtarla eşleştirilerek bulunabilen, milyonlarca ilgisiz satırın arasına gömülü bir değer olarak ele almak zorundadır.
Her değer metindir
meta_value, bir LONGTEXT sütunudur. 1299.00 fiyatı "1299.00" dizesidir. 50 olan stok adedi ise "50" dizesidir. Yazıldığı gibi geri okunan tek bir değer için bu sorun değildir; yazı meta verisinin çoğu zaten böyle kullanılır. Sıralama ya da karşılaştırma yapmanız gerektiği anda ise sorun olmaya başlar.
Fiyata göre sıralamak için veritabanı, eşleşen her değeri karşılaştırabilmeden önce metinden sayıya dönüştürmek zorundadır ve bu karşılaştırma için bir dizin kullanamaz, çünkü dizin sayıya değil metne göre oluşturulmuştur. Aynı sorun dize olarak saklanan tarihler ve “100'ün altındaki fiyat” gibi her sayısal filtre için de geçerlidir.
Birden fazla alana göre filtrelemek sorunu büyütür, çünkü her alan kendi satırında durur. Fiyatı 100'ün altında ve şehri İstanbul olan ürünleri bulmak için sorgu, wp_postmeta tablosunu alan başına bir kez kendisiyle birleştirmek (join) zorundadır; her birleştirme post_id ve ilgili meta_key üzerinden eşleştirilir:
SELECT p.ID, pm_price.meta_value AS price
FROM wp_posts p
JOIN wp_postmeta pm_price
ON pm_price.post_id = p.ID AND pm_price.meta_key = '_price'
JOIN wp_postmeta pm_city
ON pm_city.post_id = p.ID AND pm_city.meta_key = 'city'
WHERE pm_city.meta_value = 'Istanbul'
ORDER BY CAST(pm_price.meta_value AS DECIMAL(10,2));Filtrelediğiniz veya sıraladığınız her ek alan, aynı kalabalık tabloya karşı bir self-join daha demektir ve sonuçları sıralamadan önce veritabanının her aday satır için hesaplaması gereken bir CAST işlemi daha demektir.
Tipli bir tablo neyi değiştirir
Alan grubu başına bir tabloyla bir ürün on satır değil, tek satırdır. Fiyat bir DECIMAL sütunu, stok bir INT, çıkış tarihi bir DATE sütunudur. Her alan en başından beri sahip olması gereken türe sahiptir ve aynı sorgu için yazılar tablosuna tek bir join yeterlidir, dönüştürme gerekmez:
SELECT p.ID, d.price
FROM wp_posts p
JOIN wp_wpmcf_fg_product d ON d.mid = p.ID AND d.mtype = 'posts'
WHERE d.city = 'Istanbul'
ORDER BY d.price;Fiyat sayı olarak saklandığı için ona göre sıralama normal bir sayısal sıralamadır. Gerçek bir sütun olduğu için, başka herhangi bir uygulamadaki başka herhangi bir tabloda yapacağınız gibi ona veya city sütununa normal bir dizin ekleyebilirsiniz. Veritabanı tam olarak tasarlandığı işi yapar: her şeyi tarayıp yol boyunca dönüştürme hesaplamak yerine, dizini kullanarak doğrudan eşleşen satırlara atlar.
Bu SQL'i kendiniz yazmanız nadiren gerekir. wpmcf_query değerini WP_Query öğesine geçirin; eklenti grubun tablosunu sizin için birleştirir:
$products = new WP_Query( [
'post_type' => 'product',
'wpmcf_query' => [
[ 'field' => 'product.city', 'value' => 'Istanbul' ],
],
'orderby' => [ 'wpmcf:product.price' => 'ASC' ],
] );Burada hız rakamları yayımlamıyoruz. Bunlar sunucunuza, verilerinize ve dizinlerinize bağlıdır; bizim verdiğimiz bir rakama güvenmenizden çok kendi sitenizde ölçmenizi tercih ederiz. Hangi kıyaslamaya bakarsanız bakın önemli olan yapısal farktır: daha az satır, gerçek türler ve ihtiyaç duyduğunuz yerde dizinler. Söz dizimi alan değerine göre yazı sorgulama bölümünde anlatılır.
Mass Custom Fields, her alan grubu için bu tablolardan birini otomatik olarak oluşturur. Bir alanı sayı, tarih ya da kısa metin değeri olarak tanımlarsınız ve eklenti buna uygun bir sütun türü seçer; tipli bir tablo elde etmek için SQL bilmeniz gerekmez. Zaten ACF kullanıyorsanız, eklenti ACF verilerini değiştirmeden alan gruplarınızı ve kayıtlı değerlerinizi kopyalayabilir; bkz. ACF'den taşıma.
Bu farklar karşılaştırma sayfasında olgusal olarak belirtilir.
Sonuç
wp_postmeta, yazı başına birkaç ayar için yeterlidir. Tam olarak bunun için tasarlanmıştır: bir yazıya eklenen, tek tek geri okunan rastgele ve seyrek değerler. Katalogları, dizinleri ve çok sayıda satırda filtrelediğiniz ya da sıraladığınız her şey için veriye kendi tablosunu verin: tipli sütunlar, normal bir dizin ve veritabanının gerçekten optimize edebileceği bir sorgu.



