Bir WHERE yan tümcesi kullanarak veya uygulama kodu kullanarak bir resultset filtrelemek için daha iyi mi?

3 Cevap php

Tamam, burada sorunun basit bir soyutlama değildir:

Kullanıcı, yani erkek ve dişi 2 gruplarını saklamak için 2 değişken (male_users ve female_users)

  1. 1 yolu, onları seçmek için iki sorguları kullanmaktır:

select * from users where gender = 'male' ve ardından male_users de sonucu depolamak

select * from users where gender = 'female 've ardından female_users olarak sonucu depolamak

  1. Başka bir yolu yalnızca bir sorgu çalıştırmak için:

'select * from users' and then loop over the result set to filter the male users in the program php code snippet would be sth like this:

$result = mysql_query('select * from users');

while (($row=mysql_fetch_assoc(result)) != null) {
  if ($row['gender'] == 'male'){// add to male_users}
  else if ($row['gender'] == 'female'){// add to female_users}
}

hangisi daha verimli ve daha iyi bir yaklaşım olarak kabul?

Bu sorunun basit bir örneğidir. Gerçek proje sorgulamak için lager tabloları ve daha fazla filtre seçenekleri olabilir.

şimdiden teşekkürler!

3 Cevap

Filtreleme, sıralama ve birleştirme: herhangi bir uygulama için başparmak kuralı DB iyi yaptığı şeyleri yapalım olduğunu.

Kendi işlevler veya sınıf yöntemleri içine sorguları ayırın:

$men = $foo->fetchMaleUsers();
$women = $foo->fetchFemaleUsers();

Update

Ben iki ayrı endeksli sorguları olarak iki kat daha iyi performans tam bir tablo tarama sorgusu Steven'ın PostgreSQL gösteri aldı ve MySQL (fiili soru kullanılan) kullanarak taklit:

Schema

CREATE TABLE `gender_test` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `gender` enum('male','female') NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26017396 DEFAULT CHARSET=utf8

Ben bu sütunun amacıyla, ben de size bir tablo yerine bir keyfi DOUBLE değeri beklediğiniz gibi bir birincil anahtar sağlamaktır daha gerçekçi olarak bir VARCHAR (20) olmak değil cinsiyet tipini değiştirdi.

Unindexed Results

mysql> select sql_no_cache * from gender_test WHERE gender = 'male';

12995993 rows in set (31.72 sec)

mysql> select sql_no_cache * from gender_test WHERE gender = 'female';

13004007 rows in set (31.52 sec)

mysql> select sql_no_cache * from gender_test;

26000000 rows in set (32.95 sec)

Ben bu hiçbir açıklama ihtiyacı güveniyorum.

Indexed Results

ALTER TABLE gender_test ADD INDEX (gender);

...

mysql> select sql_no_cache * from gender_test WHERE gender = 'male';

12995993 rows in set (15.97 sec)

mysql> select sql_no_cache * from gender_test WHERE gender = 'female';

13004007 rows in set (15.65 sec)

mysql> select sql_no_cache * from gender_test;

26000000 rows in set (27.80 sec)

Burada gösterilen sonuçlar Steven verilerinden radically farklıdır. Endeksli sorgular iki kat daha hızlı tam bir tablo tarama gibi almost gerçekleştirin. Bu sağduyu sütun tanımları kullanarak düzgün bir dizine tablodan olduğunu. Ben de tüm PostgreSQL bilmiyorum, ancak benzer sonuçlar göstermek değil Steven'ın örnekte bazı önemli yanlış yapılandırma olmalıdır.

PostgreSQL MySQL daha iyi şeyler yapmak için itibar, ya da en azından iyi göz önüne alındığında, düzgün kullanıldığı takdirde PostgreSQL benzer bir performans sergileyeceğini daresay.

Ayrıca bu aynı makinede aşırı döngü 52 milyon karşılaştırmalar yapmak için basitleştirilmiş bir bir additional 7.3 seconds yürütmek için gereken üzerine, unutmayın.

<?php
$N = 52000000;
for($i = 0; $i < $N; $i++) {
    if (true == true) {
    }
}

Ben bu veriler ışığında daha iyi bir yaklaşım ne oldukça açık olduğunu düşünüyorum.

Ben senin DB yapmak için hiçbir neden WHERE yan tümcesi değerlendirilmesi ekstra çalışma gerçekten orada yapmak olduğunu iddia ediyorum. Eğer actually tüm kayıtları istiyorum göz önüne alındığında, bunları getiriliyor işi yapmak zorunda kalacaktır. Eğer tablodan tek bir SELECT yaparsanız, bu tablo-sırayla hepsini almak ve bunları kendiniz bölümlemek. NEREYE erkek SELECT ve WHERE kadın seçerseniz, her operasyon için bir dizin vurmak gerekir, ve bazı veri yerellik kaybedersiniz.

Diskteki kayıtlar erkek-dişi alternatif ve bellek çok daha büyük bir veri kümesi varsa, her ikisi için de tek bir SELECT bir olacaktır oysa Örneğin, büyük olasılıkla, iki ayrı sorgu yaparsanız iki kez tüm veritabanını okumak gerekir Tek tablo taraması.

EDIT: Since I'm getting downmodded into oblivion, I decided to actually run the test. I've generated a table

GEÇİCİ TABLO gender_test (some_data ÇİFT HASSAS, cinsiyet KARAKTER DEĞİŞEN (20)) CREATE;

Ben bazı rasgele veri oluşturulur,

select gender, count(*) from gender_test group by gender;
gender | count
--------+----------
female | 12603133
male | 10465539
(2 rows)

İlk olarak, ben iyiyim eminim bu durumda, en endekslerin olmadan bu testleri çalıştırmak izin ...

test=> EXPLAIN ANALYSE SELECT * FROM gender_test WHERE gender='male';
QUERY PLAN


Seq Scan on gender_test (cost=0.00..468402.00 rows=96519 width=66) (actual time=0.030..4595.367 rows=10465539 loops=1)
Filter: ((gender)::text = 'male'::text)
Total runtime: 5150.263 ms

test=> EXPLAIN ANALYSE SELECT * FROM gender_test WHERE gender='female';
QUERY PLAN


Seq Scan on gender_test (cost=0.00..468402.00 rows=96519 width=66) (actual time=0.029..4751.219 rows=12603133 loops=1) Filter: ((gender)::text = 'female'::text)
Total runtime: 5418.891 ms

test=> EXPLAIN ANALYSE SELECT * FROM gender_test;
QUERY PLAN


Seq Scan on gender_test (cost=0.00..420142.40 rows=19303840 width=66) (actual time=0.021..3326.164 rows=23068672 loops=1)
Total runtime: 4543.393 ms (2 rows)

Komik, gerçekten hızlı filtre olmadan bir tablo tarama verilerini edilir getiriliyor gibi görünüyor! Aslında, daha hızlı olarak iki kat daha! Ben tahmin Çok gibi (+ 5418> 4543 5150)! :-P

Şimdi, bir dizin yapalım ve sonuçlarını değiştirir görmek ...

Gender_test (cinsiyet) ON INDEX test_index CREATE;

Şimdi aynı sorguları yeniden çalıştırmanız ...

test=> EXPLAIN ANALYSE SELECT FROM gender_test WHERE gender='male';
QUERY PLAN


Bitmap Heap Scan on gender_test (cost=2164.69..195922.27 rows=115343 width=66) (actual time=2008.877..4388.348 rows=10465539 loops=1)
Recheck Cond: ((gender)::text = 'male'::text)
-> Bitmap Index Scan on test_index (cost=0.00..2135.85 rows=115343 width=0) (actual time=2006.047..2006.047 rows=10465539 loops=1)
Index Cond: ((gender)::text = 'male'::text)
Total runtime: 4941.64 ms

test=> EXPLAIN ANALYSE SELECT * FROM gender_test WHERE gender='female';
QUERY PLAN


Bitmap Heap Scan on gender_test (cost=2164.69..195922.27 rows=115343 width=66) (actual time=1915.385..4269.933 rows=12603133 loops=1)
Recheck Cond: ((gender)::text = 'female'::text)
-> Bitmap Index Scan on test_index (cost=0.00..2135.85 rows=115343 width=0) (actual time=1912.587..1912.587 rows=12603133 loops=1)
Index Cond: ((gender)::text = 'female'::text)
Total runtime: 4931.555 ms (5 rows)

test=> EXPLAIN ANALYSE SELECT * FROM gender_test;
QUERY PLAN


Seq Scan on gender_test (cost=0.00..457790.72 rows=23068672 width=66) (actual time=0.021..3304.836 rows=23068672 loops=1)
Total runtime: 4523.754 ms

Komik .... tek seferde tüm tabloyu tarayarak hala twice as fast! (4523 vs + 4931 4941)

NOTE bu bilimdışı yolları her türlü var. Ben 16GB RAM ile çalıştırıyorum, yani tüm veri kümesi belleğe sığar. Postgres neredeyse o kadar kullanmak üzere yapılandırılmış, ancak disk önbelleği hala diski vurdu bir kez etkiler sadece kötü olsun ki ... Ben varsayımında istiyorum yardımcı olur (ama aslında denemek için assed olamaz) değildir. Ben sadece Postgres indeksleme btree varsayılan çalıştı. Ben PHP bölümleme hiçbir zaman alır varsayarak kulüpler - doğru değil, ama muhtemelen oldukça makul bir yaklaşım.

Tüm testler Mac Pro 8-yönlü 2.66 Xeon 16GB RAID-0 7200 çalıştırmak

Ayrıca, bu veri kümesi çoğu insanın umurunda muhtemelen daha biraz daha büyük olan 26 milyon satır, bir ...

Açıkçası, ham hızı umurumda tek şey değil. (En?) Birçok uygulamada, onları ayrı ayrı getiriliyor mantıksal "doğruluğu" hakkında daha fazla önem ediyorum. "Biz daha hızlı gitmek için bu gerekiyor" diyerek patronuna aşağı gelince, bu (görünüşte) bir 2x hıza verecektir. OP açıkça verimliliği hakkında sordu. Mutlu?

Eğer, 1 milyon kullanıcı varsa, tercih (considering half of them is male, and half of the is female) do:

  • DB 1 milyon kullanıcı getiriliyor?
  • veya yalnızca DB 500k kullanıcıları getiriliyor?

Ben size sadece yarım kullanıcıları ;-) Ve durumuna bağlı olarak, daha karmaşık ise, bu daha az olabilir almak için tercih söyleyerek cevap verecektir sanırım.


Basically, fetching less data means :

  • kullanılan az ağ "Hiçbir şey için" (i.e. to fetch data that will immediatly be discarded)
  • daha az bellek özellikle PHP sunucu üzerinde, kullanılan
  • MySQL sunucusu üzerinde potansiyel olarak daha az disk erişimi - diskten almak için daha az veri olduğu gibi

Genel durumlarda, biz gereken daha fazla veri getiriliyor önlemek için deneyin; diğer bir deyişle Biz veritabanı tarafındaki filtre yerleştirin.


Of course, this means you'll have to think about the indexes you'll place on your database's tables : they'll have to fit the needs of the queries you'll be doing.