помогите с sql запросом

Главные вкладки

Аватар пользователя screenager@drupal.org screenager@drup... 19 апреля 2010 в 9:21

Здравствуйте,
помогите правильно написать запрос, на данный момент он выглядит так - <?php$count = db_result(db_query(SELECT COUNT(nid) FROM {term_node} WHERE tid ='$term->tid'));?>
т.е. подсчитывается кол-во нод с выбранным термином, а нужно сделать так чтобы подсчитывалось кол-во нод не с одним термином а с тремя, т.е. у ноды совпадает три выбранных термина
примерно это я представляю так <?php$count = db_result(db_query(SELECT COUNT(nid) FROM {term_node} WHERE tid ='$term->tid' AND tid ='$term2->tid' AND tid ='$term3->tid' ));?>

но это конечно не правильный запрос, помогите разобраться, спасибо ...

Комментарии

Аватар пользователя natbampo natbampo 19 апреля 2010 в 10:18

Мой вариант:

SELECT COUNT(DISTINCT y.nid) FROM {term_node} y
WHERE y.nid IN
(
SELECT x1.nid AS nid FROM {term_node} x1
WHERE x1.tid = 5 or x1.tid = 10 or x1.tid = 15
GROUP BY x1.nid
HAVING COUNT(x1.nid) > 2
)

- выбрать те у которых tid 5,10,15 имеется.
COUNT(x1.nid) > 2 - т.е. три совпадения чтобы было, т.к. три условия.

Аватар пользователя Siegfrid@drupal.org Siegfrid@drupal.org 19 апреля 2010 в 10:20

'$term->tid' - явно никуда не катит, т.к. в таких кавычках инфа будет передаваться в виде строки, а не свойства объекта.

Воспользуйтесь советом natbampo, если человек использует выражение с having, то ему явно можно доверять Smile

Аватар пользователя Siegfrid@drupal.org Siegfrid@drupal.org 19 апреля 2010 в 10:22

Посмотрел повнимательнее, заметил что строчка совсем какая то странная - SELECT COUNT(nid) FROM {term_node} WHERE tid ='$term->tid'). такое не прошло бы интерпретатор, выложите нормальный код или исправьте этот.

Аватар пользователя screenager@drupal.org screenager@drup... 19 апреля 2010 в 10:31

"natbampo" wrote:
"natbampo" wrote:

SELECT COUNT(DISTINCT y.nid) FROM {term_node} y
WHERE y.nid IN
(
SELECT x1.nid AS nid FROM {term_node} x1
WHERE x1.tid = 5 or x1.tid = 10 or x1.tid = 15
GROUP BY x1.nid
HAVING COUNT(x1.nid) > 2
)

- выбрать те у которых tid 5,10,15 имеется.
COUNT(x1.nid) > 2 - т.е. три совпадения чтобы было, т.к. три условия.

большое спасибо, очень помогли Smile

Аватар пользователя Dеmimurych Dеmimurych 19 апреля 2010 в 12:52

"<a href="mailto:screenager@drupal.org">screenager@drupal.org</a>" wrote:
большое спасибо, очень помогли =)

использование этого запроса возможно только в случаях если у вас крайне небольшое количество данных.

достаточно глянуть explain запроса чтобы понять какой ужас ждет сервер при попытке выолнить его.

правильно эта задача решается вот так.

<?php
db_query
'SELECT COUNT(n.nid) FROM node n 
JOIN term_node tn on tn.nid=n.nid 
JOIN term_node tn2 on tn2.nid=n.nid
JOIN term_node tn3 on tn3.nid=n.nid
where tn.tid=%d and tn2.tid=%d and tn3.tid=%d'
tidtid2tid3);?>

в случае если есть уверенность в целостности таблицы term_node запрос можно оптимизировать еще вот до такого состояния.

<?php
db_query
'SELECT COUNT(tn.nid) FROM term_node tn 
JOIN term_node tn2 on tn2.nid=tn.nid
JOIN term_node tn3 on tn3.nid=tn.nid
where tn.tid=%d and tn2.tid=%d and tn3.tid=%d'
tidtid2tid3);?>
Аватар пользователя Dеmimurych Dеmimurych 19 апреля 2010 в 12:40

"<a href="mailto:Siegfrid@drupal.org">Siegfrid@drupal.org</a>" wrote:
Воспользуйтесь советом natbampo, если человек использует выражение с having, то ему явно можно доверять Smile

нельзя. запрос составлен крайне не оптимально, без понимания того как mysql оптимизирует запросы.

Аватар пользователя olk olk 19 апреля 2010 в 15:17

"Dеmimurych" wrote:
правильно эта задача решается вот так.

Только работать не будет Smile так как зависит от порядка аргументов ... да и три самоджойна тоже не очень оптимально ...

Кстати у запроса с вложением не такой уж плохой explain как все привыкли думать

db_query("SELECT COUNT(dv.nid) AS cnt,dv.nid FROM node n INNER JOIN  (
SELECT COUNT(tn.nid) AS cnt,tn.nid
FROM term_node tn WHERE tn.tid IN (%d, %d, %d)
GROUP BY tn.nid
HAVING COUNT(tn.nid) >2
) AS dv ON n.nid=dv.nid
GROUP BY dv.nid"
, $tid1, $tid2, $tid3);
Аватар пользователя Dеmimurych Dеmimurych 10 ноября 2015 в 11:46

"olk" wrote:
Только работать не будет Smile так как зависит от порядка аргументов ... да и три самоджойна тоже не очень оптимально ...

Ну чот ж давайте разбираться. Вероятно у меня mysql не кошерный но почему то работает.
не говоря уже о том, что именно этот способ используется в taxonomy модуле.

само джоины тут соврешенно пофигу, потому что используются только поля с инедксами.
в общем случае mysql пройдется только по кешу индексов (которые лежат при верной настсройке mysql в памяти).

"olk" wrote:
Кстати у запроса с вложением не такой уж плохой explain как все привыкли думать

вы смотрели? хуже может быть только если бы индексов не было вообще.

using temporary создается лишняя временная таблица. А если обьем выходных данных велик, то и создание ее на диск.
using filesort необходим второй проход по результирующему множеству для выдачи его согласно запросу.

вместо того что в мое случае, пройтись по таблице индекса которая лежит(обычно) в оперативной памяти?

и это я еще не обратил Ваше внимание на обьем анализируемых данных, в моем случае и в случае с субзапросом.

по моему кто то кого то просто не понял.

ну и что бы добить до конца вот - на мой взгляд самый оптимальный вариант

SELECT COUNT(tn.nid) FROM term_node tn
JOIN term_node tn2 on tn2.nid=tn.nid and tn2.tid=10
JOIN term_node tn3 on tn3.nid=tn.nid and tn3.tid=15
where tn.tid=5

Аватар пользователя olk olk 19 апреля 2010 в 23:28

Да причем тут быстродействие если ваш запрос дает не правильный результат Smile

mysql> SELECT COUNT(dv.nid) AS cnt FROM node n INNER JOIN  (
    -> SELECT tn.nid
    -> FROM term_node tn WHERE tn.tid IN (223,18,197)
    -> GROUP BY tn.nid
    -> HAVING COUNT(tn.nid) >2
    -> ) AS dv ON n.nid=dv.nid;
+-----+
| cnt |
+-----+
|   4 |
+-----+
1 row in set (0.00 sec)
mysql> SELECT COUNT(tn.nid) FROM term_node tn
    -> JOIN term_node tn2 on tn2.nid=tn.nid and tn2.tid=197
    -> JOIN term_node tn3 on tn3.nid=tn.nid and tn3.tid=223
    -> where tn.tid=18;
+---------------+
| COUNT(tn.nid) |
+---------------+
|          1210 |
+---------------+
1 row in set (0.02 sec)
Аватар пользователя Dеmimurych Dеmimurych 20 апреля 2010 в 3:29

"olk" wrote:
Да причем тут быстродействие если ваш запрос дает не правильный результат :)

в моем распоряжении две базы
400 000 нод и 201 000 записей в term_data
128 000 нод и 78 000 term_data

все идет именно так как и нужно.
и четко совпадает с результатами вашего запроса.

Мой запрос может работать неправильно только в двух случаях. Оба случая говрят о том что у вас неверная таблица term_node

Случай первый:
Если в таблице term_node у вас присутствую дублирующие друг друга записи.Да это и видно из самого запроса.

проверьте есть ли у вас для одного и того же nid один и тот же tid два раза.
грубо говоря
nid tid
100 18
100 18
чего в принципе быть не должно, это ограничено индексами.

Случай второй:
либо, как я уже предупреждал, у вас в term_node присутствуют nid которых в таблице node нет

Если вы считаете что случай два это НОРМАЛЬНОЕ поведение, то воспользуйтесь моим первым запросом, с привязкой к таблице node

мой запрос абсолютно верен.

Аватар пользователя Crea Crea 20 апреля 2010 в 5:56

Dеmimurych
Ваш вариант считает ноды имеющие хотя бы один из тегов. Просто вы использовали LEFT JOIN а нужен INNER. См. мой вариант выше.
А так, в верном направлении шли..

Аватар пользователя olk olk 20 апреля 2010 в 11:31

Да вы правы, но такая ситуация ситуация возникает если нода поддерживает хранение ревизий, т.е. в принципе правильный запрос наверное будет примерно такого вида

db_query("SELECT COUNT(DISTINCT n.nid) AS cnt FROM node n
INNER JOIN term_node tn on tn.nid=n.nid AND tn.vid=n.vid AND n.status=1 AND tn.tid=%d
INNER JOIN term_node tn2 on tn2.nid=n.nid AND tn2.vid=n.vid AND tn2.tid=%d
INNER JOIN term_node tn3 on tn3.nid=n.nid AND tn3.vid=n.vid AND tn3.tid=%d"
, $tid1, $tid2, $tid3)

"Crea" wrote:
Dеmimurych
Ваш вариант считает ноды имеющие хотя бы один из тегов. Просто вы использовали LEFT JOIN а нужен INNER. См. мой вариант выше.
А так, в верном направлении шли..

Вообще-то JOIN - и предполагает INNER JOIN, но конечно лучше писать польностью Smile