[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Сложный запрос к БД
vedmed
Добрый утро, знающие господа! Помогите составить запрос к БД, никак не приходит в голову.
Ситуация такая, есть две таблицы, первая:
SQL
watch_id firm_name date_create
1 Слава 10.01.99
2 Полет 11.01.00

, т.е. в первой таблице есть ид-часов, название часов и дата создания.

Имеется вторая таблица, которая выглядит так:

SQL
watch_id param_id param_value
1 1 1
1 2 0
1 3 0
1 4 1
1 5 1000
2 1 0
2 2 1
2 3 1
2 4 0
2 5 2000

, т.е. во второй таблице: первый столбец-ид-часов(равен ид из первой таблицы), второй столбец - ид-параметра (для понятности, например параметр с ид равным 1 - автоподзавод, 2 - подсветка, 3 - секундная стрелка, 4 - секундомер, 5 -цена, ну и т.д. ), третий столбец - это значение параметра, т.е. 1 -есть, 0 -нет, для цены соответственно 1000 и 2000. Ну например для часов с ид равным 1 (Слава), есть автоподзавод, нет подсветки, нет секундной стрелки, есть секундомер,цена равна 1000 р. (Пример просто). В общем часов много может быть. Теперь вопрос: нужно выбрать все часы с функциями автоподзавода, секундомера и ценой меньше 2000 (т.е. с ид равным 1). У кого-нибудь есть идеи как составить такой запрос? У меня есть такой запрос, но
работает он не совсем верно:
SQL
SELECT t1.id_w,t1.firm_w,t1.ref_w,t1.sex_w,t1.mech_w,t1
.mat_w1,t1.mat_bras,t1.glass,t1.ph5,COUNT(*)
FROM watch_records t1
JOIN (SELECT t2.watch_id FROM watch_param t2 WHERE (t2.param_id,t2.param_value)
IN ((1,1),(4,1)) OR (t2.param_id=5 AND t2.param_value<2000)
GROUP BY t2.watch_id
HAVING
COUNT(*)=1) t3
ON (t1.id_w=t3.watch_id) WHERE t1.mech_w='Механические' AND t1.form_w='0' GROUP BY t1.id_w
LIMIT 0, 10

Может какую-нибудь функцию написать?



Спустя 1 час, 23 минуты, 46 секунд (7.06.2009 - 11:19) sergeiss написал(а):
Я посмотрел, подумал, "помедитировал"... И подумал, что по-другому надо немного.
Сначала выбираем из второй таблицы все идентификаторы часов. Там прописываем все условия на параметры, и затем группируем, чтобы получить список уникальных идентификаторов, соответствующих условиям. После чего выбираем нужные параметры из первой таблицы, указывая описанную мной выборку в качестве набора в условии where для идентификаторов.
Примерно так:
SQL
select набор_нужных_параметров_первой_таблицы from watch_records where watch_id in (select watch_id from watch_param where param_id=1 or param_id=5 or param_id=6 group by watch_id)

Условие на параметры можно как через OR вводить, так можно и через IN попробовать.
SQL
...watch_id in (select watch_id from watch_param where param_id in (1,5,6) group by watch_id)

Спустя 22 минуты, 39 секунд (7.06.2009 - 11:42) vedmed написал(а):
Спасибо, за ответ! Дело в том, что я сначала тоже пошел таким путем:
SQL
select набор_нужных_параметров_первой_таблицы from watch_records where watch_id in (select watch_id from watch_param where (param_id=1 AND param_value=1) AND (param_id=5 AND param_value=1) AND (param_id=6 AND param_value<1000) group by watch_id)
.
Но фишка в том, что это не работает mysql видит только первое условие. Может, конечно, я тебя не правильно понял? Не мог бы полный запрос привести?

Спустя 8 минут, 7 секунд (7.06.2009 - 11:50) sergeiss написал(а):
Цитата (vedmed @ 7.06.2009 - 12:42)
Не мог бы полный запрос привести?

А я и привел ПОЛНЫЙ запрос smile.gif Вот этот вот:

SQL
select набор_нужных_параметров_первой_таблицы from watch_records where watch_id in (select watch_id from watch_param where param_id=1 or param_id=5 or param_id=6 group by watch_id)

Тебе надо только указать набор параметров из первой таблицы, который ты хочешь вывести, и больше ничего не меняй (только если цифры в условии на выбор параметров). То есть, запусти запрос в более-менее простом (не сильно усложненном) варианте.
Ежели всё отработает нормально, то тогда можно будет усложнять запрос дальше, но только в одной части: где набор условий на параметры, больше нигде.

Спустя 1 час, 47 минут, 51 секунда (7.06.2009 - 13:38) vedmed написал(а):
Пустой запрос получается, по нему ничего не находит.
Смотри, по идее этот запрос:
SQL
select набор_нужных_параметров_первой_таблицы from watch_records where watch_id in (select watch_id from watch_param where param_id=1 or param_id=5 or param_id=6 group by watch_id)
Выберет ВСЕ часы. Потому как у всех часов есть param_id=1,5,6. Но мне нужно условие во втором запросе, допустим чтобы параметр с param_id=1 имел param_value=1, param_id=5 - param_value=0, a param_id=6 - param_value<2000.

Спустя 55 минут, 2 секунды (7.06.2009 - 14:33) sergeiss написал(а):
Цитата (vedmed @ 7.06.2009 - 14:38)
Пустой запрос получается, по нему ничего не находит.

Не понял... Мой запрос ничего не находит? Или он находит, но ты что-то другое имел ввиду?
Цитата (vedmed @ 7.06.2009 - 14:38)
Но мне нужно условие во втором запросе, допустим чтобы параметр с param_id=1 имел param_value=1, param_id=5 - param_value=0, a param_id=6 - param_value<2000

Если мой запрос все-таки находит данные (а он должен это делать), то тогда усложняй условие под свои потребности:
SQL
where watch_id in (select watch_id from watch_param where (param_id=1 and para_value=1) or (param_id=5 and param_value=0) or (param_id=6 and param_value < 2000) group by watch_id)

Спустя 14 минут, 47 секунд (7.06.2009 - 14:48) Guest написал(а):
1. Запрос не находит ничего
2. Мне нужно AND, т.е. те часы у которых есть (param_id=1 AND param_value=1) AND (param_id=5 AND param_value=1) AND (param_id=6 AND param_value<1000), выполнение какого-то одного условия не подходит.

Спустя 7 минут, 51 секунда (7.06.2009 - 14:55) sergeiss написал(а):
Попробуй сначала выполнитьтолько внутренний запрос, к тому же в измененном варианте. Я что-то насчет group by зациклился rolleyes.gif , а там должно быть по-другому немного:
...
Стоп! Я удалил тут то, что написал, т.к. оно неверно. Подумать надо немного.

Спустя 9 минут, 41 секунда (7.06.2009 - 15:05) sergeiss написал(а):
Давай думать.
Условие (param_id=1 AND param_value=1) AND (param_id=5 AND param_value=1) AND (param_id=6 AND param_value<1000) будет ВСЕГДА давать пустоту, т.к. оно относится к одной строке. А в ней никогда не может быть одновременно param_id=1 и param_id=5.
Вопрос: набор параметров (наличие/отсутствие автоподзавода, подсветки, секундной стрелки и т.д.) у тебя постоянный, не будет часто меняться? Если не часто, то тогда я бы предложил именить структуру второй таблицы. Проще будет находить данные.

Спустя 12 минут, 53 секунды (7.06.2009 - 15:18) sergeiss написал(а):
Структура второй таблицы "просится" в виде:
watch_id param_light param_auto param_price ... и так далее до полного списка возможных парамеров. Проще и нагляднее будет делать выборку.

А если работать с существующей структурой данных, то, фактически, подобную таблицу надо создавать "на лету", с количеством парамеров равным искомому в данный момент. Запросом типа указанного ниже, в данном примере для 3-х параметров (если искомых параметров больше одного):
SQL
select w1.watch_id
from watch_param w1
left join watch_param w2 on w1.watch_id = w2.watch_id
left join watch_param w3 on w1.watch_id = w3.watch_id
where w1.param_id=1 and w1.param_value=1 and w2.param_id=5 and w2.param_value=2 and w3.param_id=6 and w3.param_value<2000

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

И далее уже этот запрос использовать как внутренний в том запросе, который я писал ранее

Спустя 6 часов, 9 минут, 16 секунд (7.06.2009 - 21:27) vedmed написал(а):
sergeiss, спасибо за участие! Дело в том, что параметров где-то 100-110. Поэтому делать таблицу с таким количеством столбиков не рационально. Вот запрос который работает, но он мне не очень нравится:
SQL
SELECT t1.id_w,t1.firm_w,t1.ref_w,t1.sex_w,t1.mech_w,t1.mat_w1,t1.mat_bras,t1.glass
,t1.ph6,COUNT(*)
FROM watch_records t1
JOIN (SELECT t2.watch_id FROM watch_param t2 WHERE (t2.param_id,t2.param_value)
IN ((17,0))
GROUP BY t2.watch_id
HAVING
COUNT(*)=1) t3
ON (t1.id_w=t3.watch_id) WHERE (param_id=95 AND param_value>1000) AND t1.mech_w='Кварцев
ые' AND t1.sex_w='0' AND t1.form_w='0' AND t1.mat_w1='Золото' GROUP BY t1.id_w LIMIT 0, 10
.
В том запросе что предложил ты опять в строчку идут (param_id=1 AND param_value=1) AND (param_id=5 AND param_value=1) AND (param_id=6 AND param_value<1000)...

Спустя 17 минут, 21 секунда (7.06.2009 - 21:45) sergeiss написал(а):
Во-первых, мне непонятно вот что:
Цитата (vedmed @ 7.06.2009 - 22:27)
Поэтому делать таблицу с таким количеством столбиков не рационально.

Почему нерационально-то? Ты считаешь, что рациональнее "кувыркаться" с выборкой параметров? biggrin.gif

А во-вторых - ты не сказал, попробовал ли мой запрос? И если да, то что из этого получилось. А то, что там, в моем запросе, идут подряд параметры, в данном случае нормально. Там же не те параметры, что ты написал, а их представители из (формально) разных таблиц w1, w2 и w3.

И в-третьих, я пытался разобраться в твоем запросе - ничего не понял! Хотя, возможно, это я такой непонятливый? smile.gif

Спустя 6 дней, 23 часа, 12 минут, 52 секунды (14.06.2009 - 20:57) vedmed написал(а):
Спустя неделю дошли руки.
"Почему нерационально-то?" - я так думаю, что база данных будет быстрее выполнять операции с таблицами которые, имеют много строчек, но не много столбцов. Это не так? Попробую разобраться в этом.
sergeiss, твой запрос, к сожалению, не работает, он по нему ничего не выводит.

Спустя 3 дня, 15 часов, 17 минут, 38 секунд (18.06.2009 - 12:15) vedmed написал(а):
Появился такой вариант решения проблемы, запрос работает:
SQL
SELECT t1.id_w,t1.firm_w FROM watch_records t1
WHERE t1.id_w IN ( SELECT watch_id FROM watch_param WHERE (param_id,param_value) IN ((17,1),(38,1)) AND watch_id IN (SELECT watch_id FROM watch_param WHERE param_id = 20 AND param_value BETWEEN 10 AND 100));


Спустя 1 час, 29 минут, 41 секунда (18.06.2009 - 13:45) Sylex написал(а):
param_id = 17, 38 и 20

по-моему этот запрос не должен ни одной записи возвращать

Спустя 15 минут (18.06.2009 - 14:00) glock18 написал(а):
Цитата (Sylex @ 18.06.2009 - 10:45)
по-моему этот запрос не должен ни одной записи возвращать

По-моему, должен. Если я правильно понял, то там сначала выбираются записи с param_id = 20 и берется их watch_id (id часов что ли?). После этого из всех этих часов берутся те, у которых param_id 17 = 1, либо 38 = 1.

Проще говоря, я себе это так представил.

Будут найдены все часы ценой от 10 до 100 $ и, либо с красненькой крышечкой, либо с блестящим ремешочком (логический OR). Вот примерно так. кажется, нормально.

Спустя 25 секунд (18.06.2009 - 14:00) sergeiss написал(а):
Цитата (Sylex @ 18.06.2009 - 14:45)
param_id = 17, 38 и 20

по-моему этот запрос не должен ни одной записи возвращать

У него тут более хитрая комбинация параметров, так что записи могут возвращаться, и даже не одна. Естественно, при их наличии.
Тут же смотри как: комбинация 2-х параметров (param_id,param_value) должна быть одной из списка (а не один параметр!!!), и одновременно watch_id должен быть одним из списка.

В самом последнем подзапросе я бы поставил distinct:
SQL
(SELECT distinct watch_id FROM watch_param WHERE param_id = 20 AND param_value BETWEEN 10 AND 100)

Спустя 2 часа, 32 минуты, 44 секунды (18.06.2009 - 16:33) vedmed написал(а):
"param_id = 17, 38 и 20

по-моему этот запрос не должен ни одной записи возвращать"

Нормально работает. Подзапрос:
Вытащить все ид-часов для которых справедливо равенство (param_id=17 и param_value=1) или (param_id=38 и param_value=1) (подобный способ называется конструктор строки) и ид часов, с param_id=20 у которого param_value находится в промежутке 10<param_value<100. В итоге на выходе большого подзапроса у нас появляются id часов из второй таблицы.
А дальше все просто:
SQL
SELECT id_w,form_w FROM watch_records WHERE id_w IN (1,2,3,4,5)
...и т.д.

Можно указать еще
SQL
GROUP BY watch_id HAVING COUNT(*)=2
тогда будет только если (param_id=17 и param_value=1) и (param_id=38 и param_value=1).

Спустя 28 минут, 59 секунд (18.06.2009 - 17:02) glock18 написал(а):
sergeiss
vedmed
Зачет)) мы все одно и то же написали почти))

sergeiss
я, как и на официальном сайте mysql, советую использовать group by вместо distinct, когда это возможно wink.gif Здесь возможно

Спустя 2 минуты, 8 секунд (18.06.2009 - 17:04) vedmed написал(а):
biggrin.gif Буду теперь оптимизировать запрос.

Спустя 18 минут, 13 секунд (18.06.2009 - 17:22) sergeiss написал(а):
Цитата (glock18 @ 18.06.2009 - 18:02)
советую использовать group by вместо distinct

А вот отсюда поподробнее, плз!
Вопрос: нафига это нада? Это же разные фичи. Группировка нужна, когда ты что-то делаешь с часть параметров. Например, суммируешь, находишь максимальное-минимальное-среднее... А distinct - это просто выборка уникальных значений.

Спустя 1 час, 37 минут, 2 секунды (18.06.2009 - 18:59) glock18 написал(а):
Цитата (sergeiss @ 18.06.2009 - 14:22)
А вот отсюда поподробнее, плз!

Да. Когда-то видел что-то подобное, но теперь к сожалению не смог найти подтверждения своим словам по этому поводу.

Однако, все же поправлю тебя, потому что DISTINCT - частный случай GROUP BY. я вообще distinct с определенных пор не использую, потому что он достаточно ущербен - по сути эквивалентен GROUP BY по всем полям в SELECT. в принципе, и в выше приведенном запросе это может повлиять ненужным образом.

Важно, что с group by вовсе не обязательно делать какие-то COUNT и т.п.

Вот ссылочка с первоисточника, надеюсь, будет полезна:
http://dev.mysql.com/doc/refman/5.0/en/dis...timization.html

Спустя 16 минут, 48 секунд (18.06.2009 - 19:16) sergeiss написал(а):
Цитата (glock18 @ 18.06.2009 - 19:59)
Однако, все же поправлю тебя, потому что DISTINCT - частный случай GROUP BY. я вообще distinct с определенных пор не использую, потому что он достаточно ущербен - по сути эквивалентен GROUP BY по всем полям в SELECT. в принципе, и в выше приведенном запросе это может повлиять ненужным образом.

Ты знаешь.... Возможно, что для MySQL это и верно. Но для PostgreSQL - не верно.
Потому что в Постгре есть дополнительные возможности работы с distinct. Вот пример из хэлпа:
SQL
SELECT DISTINCT ON (location) location, time, report
FROM weather_reports ORDER BY location, time DESC;

Выбрать 3 поля, но уникальным должно быть только одно из них. Часть данных будет утеряна, конечно. Но в каких-то случаях это может быть и не принципиально. Например, нам нужно выбрать по одной уникальной точке (как в примере), вне зависимости от времени и информации о точке.

А теперь вопрос: сделаешь это же через GROUP BY? wink.gif

PS. И это не просто абстракция, а работающая конструкция. Которую можно использовать, когда это нужно.

PPS. Из твоего первоисточника: "In most cases, a DISTINCT clause can be considered as a special case of GROUP BY." То есть, во многих (но не во всех) случаях DISTINCT можно рассматривать как особый случай GROUP BY.
А я все равно буду всегда использовать DISTINCT, чтобы в явном виде показывать (хотя бы даже самому себе), что я выбираю уникальные строки. Потому что когда возвращаешься к сложному запросу через 2-3-4 месяца, то надо еще вспомнить, почему делал именно так, а не иначе. И запутывать самому себя - нет уж smile.gif, увольте!

Спустя 1 час, 35 минут, 10 секунд (18.06.2009 - 20:51) Sylex написал(а):
туплю поди... но опять вижу что всегда будет 0 записей... я понимаю если б там OR было вместо AND. ну да ладно, посмотрю завтра на свежую голову, спать хочу smile.gif

Спустя 6 минут, 16 секунд (18.06.2009 - 20:58) Sylex написал(а):
догнал.

вот это разве ни одно и то же?

SQL
SELECT t1.id_w,t1.firm_w
FROM watch_records t1
WHERE t1.id_w IN (
SELECT watch_id FROM watch_param WHERE ( (param_id,param_value) IN ((17,1),(38,1)) ) OR ( param_id = 20 AND param_value BETWEEN 10 AND 100)
);

Спустя 23 секунды (18.06.2009 - 20:58) glock18 написал(а):
Цитата (sergeiss @ 18.06.2009 - 16:16)
Потому что в Постгре есть дополнительные возможности работы с distinct. Вот пример из хэлпа:

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

Цитата (sergeiss @ 18.06.2009 - 16:16)
Выбрать 3 поля, но уникальным должно быть только одно из них. Часть данных будет утеряна, конечно. Но в каких-то случаях это может быть и не принципиально. Например, нам нужно выбрать по одной уникальной точке (как в примере), вне зависимости от времени и информации о точке.

А теперь вопрос: сделаешь это же через GROUP BY? wink.gif


просто:

SQL
SELECT DISTINCT ON (location) location, time, report
FROM weather_reports ORDER BY location, time DESC;


->

SQL
SELECT location, time, report
FROM weather_reports GROUP BY location ORDER BY location, time DESC;


увы я долго как-то побился лбом об эту проблему с distinct, вот он у меня и отложился таким smile.gif в постгре он явно получше сделан.

а насчет most cases - оно то, конечно так, только думаю, что наш случай как раз входит в эти most cases, а случай когда он не входит, я, честно говоря, сходу не могу привести smile.gif

Думаю, стоит закрыт вопрос сравнения group by и distinct, либо перенести его в соответствующую тему wink.gif хотя, по-моему, уже во всем разобрались.

Спустя 9 минут, 53 секунды (18.06.2009 - 21:08) sergeiss написал(а):
Давай все-таки закончим разговор насчет DISTINCT и GROUP BY smile.gif Я думаю, что автор темы не будет против, т.к. и он тоже получит новую информацию.
Если ты попробуешь запустить этот запрос в Постгре,
SQL
SELECT location, time, report
FROM weather_reports GROUP BY location ORDER BY location, time DESC;

то получишь ошибку
Код
ERROR:  column "weather_reports.time" must appear in the GROUP BY clause or be used in an aggregate function

Тут будет time, потому что оно, это поле, следующее по порядку после location.

Вывод: надо использовать всяки фичи там и для того, для чего они придуманы. И нефиг выёживаться wink.gif

Спустя 6 минут, 27 секунд (18.06.2009 - 21:14) glock18 написал(а):
sergeiss, лады убедил smile.gif в mysql не буду проверять - возможно, оно и там так)))

distinct в postgre рулит.
в mysql distinct не рулит. ИМХО smile.gif все таки он там не такой гибкий, как хотелось бы

Спустя 1 час, 26 минут, 26 секунд (18.06.2009 - 22:41) vedmed написал(а):
Только подумал я, что все печали позади, как не тут то было...
В общем запросик вот такой:
SQL
SELECT t1.id_w,t1.firm_w FROM watch_records t1
WHERE t1.id_w IN (SELECT watch_id total FROM watch_param WHERE (param_id,param_value) IN ((17,1),(38,1)) AND watch_id IN (SELECT watch_id FROM watch_param WHERE param_id = 20 AND param_value BETWEEN 10 AND 100)
GROUP BY watch_id HAVING COUNT(*) >=2
) AND t1.sex_w='0' AND t1.form_w='0' LIMIT 0, 10;

Работает отлично, пока... база маленькая. Стоит ей вырсти начнутся проблемы, сделал эксплейн, показал мне вот что:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL 18 Using where
2 DEPENDENT SUBQUERY watch_param index watch_id 5 1265 Using where
3DEPENDENT SUBQUERY watch_param ref watch_id,param_id param_id 5 const 18 Using where

Как смог изобразил... Вопрос вот в чем: по первой таблице нужно назначить индекс, я его назначил как id_w, но в запросе он почемуто- не участвует... И второе примения уловие GROUP BY база просматривает 1265 строчек, это все строчки из таблицы 2 как бы это оптимизировать...? Может есть у кого-нибудь идеи?

Спустя 13 минут, 15 секунд (18.06.2009 - 22:54) vedmed написал(а):
Не куя не получается...

Спустя 24 минуты, 4 секунды (18.06.2009 - 23:18) Alchemist написал(а):
Блин, люди... ну когда вы читать научитесь ? Уж сколько раз я просил sergeiss'a: "Ну не пиши ты подзапросы в IN условиях, не учи людей плохому !" Как об стену горох...

Цитата ("SQL_Manual")
outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

MySQL evaluates queries “from outside to inside.” That is, it first obtains the value of the outer expression outer_expr, and then runs the subquery and captures the rows that it produces.


вот тут можно найти несколько способов избежать этого: http://dev.mysql.com/doc/refman/5.0/en/in-...timization.html
впрочем самый простой там почему-то не указан (по-крайней мере я не нашел) - перенести подзапрос из WHERE в FROM с последущим JOIN'ом.

Спустя 35 минут, 43 секунды (18.06.2009 - 23:54) vedmed написал(а):
А по запросу ничего сказать не можешь? Тут не IN, тут GROUP BY все портит, а без него запрос не полный...

Спустя 1 час, 28 минут, 28 секунд (19.06.2009 - 01:22) kirik написал(а):
Что-то вы реально мутите с подзапросами...
Дабы не быть голословным предлагаю такое решение:
допустим ищем часы с такими параметрами: есть автоподзавод (параметр 1=1), нет подсветки (параметр 2=0), нет секундной стрелки (параметр 3=0), есть секундомер (параметр 4=1), цена меньше 2000р (параметр 5<2000) - всего 5 условий. Тоесть по всем этим условиям значения должны совпасть.
Составляем запрос:
SQL
SELECT `w`.`watch_id`, `w`.`firm_name`
    FROM `params` AS `p`
    LEFT JOIN `watch` AS `w` ON `p`.`watch_id` = `w`.`watch_id`
        WHERE (`p`.`param_id` = 1 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 2 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 3 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 4 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 5 AND `p`.`param_value` < 2000)


Результат допустим такой:
Цитата
1  Слава
1  Слава
1  Слава
1  Слава
1  Слава
2  Полет


Имеем 5 результатов по часам с id=1 "Слава" и один результат по часам "Полет"
Так как искомых параметров у нас всего 5, то часы "Слава" подходят к каждому из параметров, рождается запрос
SQL
SELECT `w`.`watch_id`, `w`.`firm_name`, COUNT(`w`.`watch_id`)
    FROM `params` AS `p`
    LEFT JOIN `watch` AS `w` ON `p`.`watch_id` = `w`.`watch_id`
        WHERE (`p`.`param_id` = 1 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 2 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 3 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 4 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 5 AND `p`.`param_value` < 2000)
            GROUP BY `p`.`watch_id`


Получаем
Цитата
1  Слава   5
2  Полет  1


Тоесть как я написал выше - часы с "Слава" с id = 1 совпали 5 раз (по всем параметрам), а часы "Полет" c id = 2 совпали всего один раз. Теперь просто добавим воды HAVING чтобы выбрать только те записи, которые совпали все 5 раз:
SQL
SELECT `w`.`watch_id`, `w`.`firm_name`
    FROM `params` AS `p`
    LEFT JOIN `watch` AS `w` ON `p`.`watch_id` = `w`.`watch_id`
        WHERE (`p`.`param_id` = 1 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 2 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 3 AND `p`.`param_value` = 0) OR
            (`p`.`param_id` = 4 AND `p`.`param_value` = 1) OR
            (`p`.`param_id` = 5 AND `p`.`param_value` < 2000)
            GROUP BY `p`.`watch_id`
                HAVING COUNT(`w`.`watch_id`) = 5


Ура! Получаем искомые "Слава"! smile.gif
С помощью этого запроса можно реализовать такую фишку как "Мы ничего не нашли, но у нас есть часы, которые могут вас заинтересовать:..." изменив HAVING COUNT(`w`.`watch_id`) = 5 например на HAVING COUNT(`w`.`watch_id`) > 3, тогда выведутся наиболее похожие часы.

PROFIT! smile.gif

ЗЫ. Индекс должен быть уникальным и захватывать 2 столбца: `param_id` и `param_value`, тогда скорость выборки будет ппц какая скорая wink.gif
Приведу на всякий случай свои таблицы:
Свернутый текст
SQL
--
-- Структура таблицы `params`
--

CREATE TABLE IF NOT EXISTS `params` (
`watch_id` smallint(5) unsigned NOT NULL,
`param_id` tinyint(3) unsigned NOT NULL,
`param_value` smallint(5) unsigned NOT NULL,
UNIQUE KEY `watch_id` (`watch_id`,`param_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Дамп данных таблицы `params`
--

INSERT INTO `params` (`watch_id`, `param_id`, `param_value`) VALUES
(1, 1, 1),
(1, 2, 0),
(1, 3, 0),
(1, 4, 1),
(1, 5, 1000),
(2, 1, 1),
(2, 2, 1),
(2, 3, 1),
(2, 4, 0),
(2, 5, 2000);

-- --------------------------------------------------------

--
-- Структура таблицы `watch`
--

CREATE TABLE IF NOT EXISTS `watch` (
`watch_id` smallint(5) unsigned NOT NULL auto_increment,
`firm_name` varchar(100) NOT NULL,
`date_create` varchar(8) NOT NULL,
PRIMARY KEY (`watch_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

--
-- Дамп данных таблицы `watch`
--

INSERT INTO `watch` (`watch_id`, `firm_name`, `date_create`) VALUES
(1, 'Слава', '10.01.99'),
(2, 'Полет', '11.01.00');

Спустя 1 час, 55 минут, 3 секунды (19.06.2009 - 03:17) Alchemist написал(а):
Цитата (vedmed @ 18.06.2009 - 22:54)
А по запросу ничего сказать не можешь? Тут не IN, тут GROUP BY все портит, а без него запрос не полный...


а, сорри, я думал тебя смущает ALL в EXPLAIN'e... По запросу я согласен с kirik'ом. При такой структуре базы лучше не сделаешь.

Спустя 4 часа, 48 минут, 8 секунд (19.06.2009 - 08:05) Sylex написал(а):
я про то и говорю, что запрос с подзапросами муторный какой-то, так что я оспорю оценку glock18 smile.gif

kirik
+100!

вник, расписал все хорошо wink.gif

Спустя 25 минут, 31 секунда (19.06.2009 - 08:31) glock18 написал(а):
А лично я понял требование по-другому huh.gif
Там нужно обязательно, чтобы больше одного параметра удовлетворило. Приведенный пример - только 1 любой.

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

Спустя 22 минуты, 38 секунд (19.06.2009 - 08:54) sergeiss написал(а):
Цитата (Alchemist @ 19.06.2009 - 00:18)
Уж сколько раз я просил sergeiss'a: "Ну не пиши ты подзапросы в IN условиях, не учи людей плохому !"

Ну, я не помню, чтобы мы на эту тему дискутировали... Но в любом случае - что же в этом плохого? Или те, кто эту хрень создавал, были глупее нас с тобой? Вряд ли.
С другой стороны. По твоей ссылке вообще нету ни слова про скорость обработки!!! Ну, кроме случая NULL IN (SELECT ........). Который мы не рассматриваем.
Или есть какие-то другие соображения, не касающиеся скорости обработки?

В случае использования IN мы сначала находим полный перечень уникальных значений (подчёркиваю - уникальных), после чего проверяем, есть ли среди них то, что нам надо.
В ином случаяе, при использовании WHERE, мы проделаем всё то же самое, только в немного другом порядке.

PS. Плюс ко всем сказанному мной ранее: использование IN позволяет сделать запрос более ясно читаемым, т.к. как в явном виде запрашивается то действие, которое требуется.

Спустя 3 минуты, 46 секунд (19.06.2009 - 08:57) glock18 написал(а):
Я бы привел другой довод. Вообще то рекомендуется (да просите меня ссылку показать, но мне сейчас лень искать tongue.gif ) заменять по возможности IN на JOIN. Это довольно часто можно сделать, но лично я не вижу здесь такой возможности. Приведенный пример не будет работать так, как того требуется, если правильно понял задачу.

Спустя 2 часа, 2 минуты, 13 секунд (19.06.2009 - 10:59) vedmed написал(а):
Цитата (glock18 @ 19.06.2009 - 05:31)
А лично я понял требование по-другому huh.gif
Там нужно обязательно, чтобы больше одного параметра удовлетворило. Приведенный пример - только 1 любой.

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

Абсолютно согласен!

Именно это и тербуется,если написать OR, то будет выбрана любая запись удовлетворяющая хотя бы одному условию, а мне нужно чтобы была запись, удовлетворяющая ВСЕМ условиям. Ну представьте вы пришли в магазин и выбираете часы, вы говорите, хочу: механические, с автоподзаводом, круглые, с секундомером и т.п. А вам говорят: вот курглые, вот механические, вот с секундомером, но все это разные часы, а вам нужно чтобы все это было в одних часах!

Спустя 7 минут, 46 секунд (19.06.2009 - 11:07) sergeiss написал(а):
Цитата (glock18 @ 19.06.2009 - 09:31)
По-моему, я задачу понял совершенно отличным образом от остальных.

Нифига smile.gif Это только я могу так сказать wink.gif Ибо уже где-то в середине 1-й страницы об этом говорил.

Спустя 21 минута, 48 секунд (19.06.2009 - 11:29) glock18 написал(а):
sergeiss, я тоже писал уже. правда, на второй, кажется, странице. smile.gif Значит не только я так понял smile.gif

Спустя 10 минут, 17 секунд (19.06.2009 - 11:39) vedmed написал(а):
Да, мастер, Kirik!

Спасибо большое.
Вот запрос:
SQL
SELECT w.id_w, w.firm_w
FROM watch_records AS w
LEFT JOIN watch_param AS p ON (w.id_w = p.watch_id)
WHERE (p.param_id = 17 AND p.param_value = 1) OR (p.param_id = 38 AND p.param_value = 1) OR (p.param_id = 20 AND p.param_value < 60)
GROUP BY p.watch_id
HAVING COUNT(p.watch_id) = 3 LIMIT 1, 10

Работает нормально. Спасибо. Ухх... ну теперь надеюсь то все. biggrin.gif Спасибо всем большое, все были в каком-то степени правы. Но kirik респект.

Спустя 7 часов, 30 минут, 28 секунд (19.06.2009 - 19:10) Alchemist написал(а):
sergeiss, если бы ты прочитал дальше процитированого предложения, ты бы увидел, что ты не совсем прав вот тут:
Цитата (sergeiss @ 19.06.2009 - 07:54)
В случае использования IN мы сначала находим полный перечень уникальных значений (подчёркиваю - уникальных), после чего проверяем, есть ли среди них то, что нам надо.


позволю себе еще раз процитировать мануал:
Цитата (Alchemist @ 18.06.2009 - 22:18)
outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

MySQL evaluates queries “from outside to inside.” That is, it first obtains the value of the outer expression outer_expr, and then runs the subquery and captures the rows that it produces.


другими словами, мускуль не "находит сначала перечень уникальных ид", а как раз наоборот, берет из внешней таблицы каждую строку, вытаскивает из нее id, и только после этого запускает подзапрос (для каждой строки !!!) с дополнительным условием. ("внутренний ид" = "внешний ид").

Именно поэтому если ты посмотришь EXPLAIN, который дал ведмедь, ты увидишь что по внешней таблице происходит table scan.

ЗЫ: да, да, я знаю, постгре - лучше, мускуль - отстой smile.gif

ЗЗЫ: а почему эта тема в "Администрировании БД" ?

Спустя 4 дня, 19 часов, 39 минут, 43 секунды (24.06.2009 - 14:50) vedmed написал(а):
Господа, а еще вопрос в тему, кто-нибудь знает как посчитать количество часов, удовлетворяющих заданному условию:
SQL
SELECT COUNT(*) FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=20 AND t2.param_value > 0)
GROUP BY (t2.watch_id)
HAVING
COUNT(t2.watch_id)=1

Выдает в результате:
SQL
COUNT(*)
1
1
1
1
1
- т.е. количество строк соответствует количеству часов, удовлетворяющих заданному условию.


Спустя 2 минуты, 42 секунды (24.06.2009 - 14:52) glock18 написал(а):
эээ... а убрать group by и having? smile.gif

Спустя 1 час, 35 секунд (24.06.2009 - 15:53) vedmed написал(а):
В том запросе,что привел, прокатит.
А если будет так:
SQL
SELECT COUNT(*) FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=20 AND t2.param_value > 0) OR (t2.param_id=19 AND t2.param_value =1)
GROUP BY (t2.watch_id)
HAVING
COUNT(t2.watch_id)=2


В этом уже надо будет узнать сколько часов, которые удовлетворяют обоим условиям. sad.gif

Спустя 17 минут, 20 секунд (24.06.2009 - 16:10) glock18 написал(а):
посчитать общее количество часов?

Цитата
HAVING
COUNT(t2.watch_id)=2


с этим никак не вяжется. тебе все записи будут отданы, где COUNT = 2 в любом случае. а без group by такой count станет бесполезен.


Спустя 27 минут, 24 секунды (24.06.2009 - 16:38) vedmed написал(а):
Угу, нужно посчитать сколько часов удовлетворяют этим условиям.Как же быть? Есть какие-нибудь идеи?

Спустя 16 минут, 36 секунд (24.06.2009 - 16:54) glock18 написал(а):
ну сразу приходит только SUM вместо COUNT в select'е вместо с group by и having

Спустя 32 секунды (24.06.2009 - 16:55) glock18 написал(а):
SUM() каунтов, понятно

PS: только я не уверен нормально ли будет воспринято SUM(COUNT(*))... помнится у меня мускул ругался на подобную конструкцию.


Спустя 11 минут, 5 секунд (24.06.2009 - 17:06) vedmed написал(а):
Не получается, такой запрос:
SQL
SELECT SUM(COUNT(*)) FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=20 AND t2.param_value > 0) OR (t2.param_id=19 AND t2.param_value =1)
GROUP BY (t2.watch_id)
HAVING
COUNT(t2.watch_id)=2
Ошибку выдает.

Спустя 1 час, 40 минут, 23 секунды (24.06.2009 - 18:46) kirik написал(а):
Что-то не до конца понял, что именно нужно.. Если нужно посчитать сколько всего часов вернул запрос, то mysql_num_rows() в помощь, а если нужно посчитать кол-во часов для того, чтобы определиться сколько страниц выводить, то SQL_CALC_FOUND_ROWS поможет:

SQL
SELECT SQL_CALC_FOUND_ROWS * FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=20 AND t2.param_value > 0) OR (t2.param_id=19 AND t2.param_value =1)
GROUP BY (t2.watch_id)
HAVING
COUNT(t2.watch_id)=2;

SELECT FOUND_ROWS();

Спустя 1 час, 16 минут, 4 секунды (24.06.2009 - 20:02) glock18 написал(а):
Не, ему надо другое. Странный вообще запрос. До сих пор чувствую, что не совсем понимаю зачем он, но тут дело не в mysql_num_rows. даже умным счетчиком)))

vedmed, еще один вариант, но он явно затормозит твой запрос - вынести SUM на select выше. то есть обернуть это все в еще один запрос и взять sum там.

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

Спустя 2 часа, 18 минут, 7 секунд (24.06.2009 - 22:20) vedmed написал(а):
Цитата (kirik @ 24.06.2009 - 15:46)
Что-то не до конца понял, что именно нужно.. Если нужно посчитать сколько всего часов вернул запрос, то mysql_num_rows() в помощь, а если нужно посчитать кол-во часов для того, чтобы определиться сколько страниц выводить, то SQL_CALC_FOUND_ROWS поможет:

SQL
SELECT SQL_CALC_FOUND_ROWS *  FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=20 AND t2.param_value > 0) OR (t2.param_id=19 AND t2.param_value =1)
GROUP BY (t2.watch_id)
HAVING
COUNT(t2.watch_id)=2;

SELECT FOUND_ROWS();

kirik в точку попал, мне нужно посчитать кол-во часов, чтобы выполнить постраничную навигацию.

P.S. Прочитал про эту функцию, qlock18 спасибо, что отозвался! kirik спасибо за точный и правильный ответ. Для тех кому интересно, нашел полезное описание этой функции: http://valera.ws/2007.08.29~sql_calc_found_rows/.

Спустя 5 минут, 40 секунд (24.06.2009 - 22:26) glock18 написал(а):
kirik, ну даешь smile.gif

Спустя 10 минут, 43 секунды (24.06.2009 - 22:37) kirik написал(а):
Цитата (glock18 @ 24.06.2009 - 14:26)
kirik, ну даешь

Сам не ожидал blink.gif

Спустя 3 минуты, 18 секунд (24.06.2009 - 22:40) glock18 написал(а):
kirik вы vedmed'ом друг друга хорошо понимаете smile.gif

Спустя 3 дня, 14 часов, 31 минута, 7 секунд (28.06.2009 - 13:11) vedmed написал(а):
kirik, а еще один вопрос, мне нужно получить вывод формата:

Слава (10)
Полет (5)

Т.е. определить сколько часов какой фирмы, удовлетворяет определенным условиям. Вот запрос:
SQL
SELECT firm_w,COUNT(*) FROM watch_records GROUP BY firm_w

Но когда добавляем условия из второй таблицы:
SQL
SELECT t1.firm_w,COUNT(*) FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=72 AND param_value='0') OR (t2.param_id=95 AND t2.param_value BETWEEN
1000 AND 2000) OR (t2.param_id=96 AND t2.param_value=1) GROUP BY t1.firm_w HAVING COUNT(t1.firm_w)
>=3;

Как обсуждалось выше COUNT(*) здесь не походит. SQL_CALC_FOUND_ROWS тоже не подойдет. У тебя нет идей как можно здесь поступить?

Спустя 6 часов, 15 минут, 7 секунд (28.06.2009 - 19:26) kirik написал(а):
Цитата (vedmed @ 28.06.2009 - 05:11)
У тебя нет идей как можно здесь поступить?

А тож smile.gif

SQL
SELECT `g`.`firm_w`, COUNT(*) AS `num` FROM ( SELECT t1.firm_w FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=72 AND param_value='0') OR (t2.param_id=95 AND t2.param_value BETWEEN 1000 AND 2000) OR (t2.param_id=96 AND t2.param_value=1)
GROUP BY t1.firm_w HAVING COUNT(t1.firm_w) >=3
) AS `g` GROUP BY `g`.`firm_w`


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

Спустя 14 часов, 27 минут, 52 секунды (29.06.2009 - 09:54) vedmed написал(а):
kirik, если я при установлении соединения с базой сделаю:
SQL
SET GLOBAL query_cache_type=1;

этого будет достаточно?

Или лучше:
SQL
SELECT SQL_CACHE `g`.`firm_w`, COUNT(*) AS `num` FROM ( SELECT t1.firm_w FROM watch_records t1
LEFT JOIN watch_param t2 ON (t1.id_w=t2.watch_id)
WHERE (t2.param_id=72 AND param_value='0') OR (t2.param_id=95 AND t2.param_value BETWEEN 1000 AND 2000) OR (t2.param_id=96 AND t2.param_value=1)
GROUP BY t1.firm_w HAVING COUNT(t1.firm_w) >=3
) AS `g` GROUP BY `g`.`firm_w`


Но фишка в том, что запрос будет меняться, в зависимости от выбранных параметров.

Спустя 14 минут, 58 секунд (29.06.2009 - 10:09) glock18 написал(а):
думаю, Кирик имел ввиду, что кэшировать надо результат запроса уже на сервере.

но можно и так, в принципе, как ты предложил. На мой взгляд, второй вариант предпочтительнее.

Спустя 23 минуты, 23 секунды (29.06.2009 - 10:32) vedmed написал(а):
Фишка в том, что результат будет меняться в зависимости от выбранных параметров, т.е. допустим "цена": сначала пользователь указал от 100 - 1000, потом передумал и указал от 1000 - 2000. Т.е. такой запрос будет меняться быстро, мне кажется, если кэшировать его, то лучше на стороне базы данных.

Спустя 19 минут, 40 секунд (29.06.2009 - 10:52) glock18 написал(а):
а, ну да. только я по правде говоря не уверен, что ты правильно понимаешь кэширование на стороне mysql сервера.

он, если не ошибаюсь, тоже просто запоминает результат запроса.

я для сложного поиска обычно завожу хранимую процедуру, где можно сделать это не только одним запросом, а разбить на два. Это на скорости плохо не скажется, потому что все на сервере произойдет. Плюс, процедура компилируется и оптимизируется один раз только - при создании, а это увеличит скорость немало.

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

Спустя 47 минут, 24 секунды (29.06.2009 - 11:40) vedmed написал(а):
Цитата (glock18 @ 29.06.2009 - 07:52)
а, ну да. только я по правде говоря не уверен, что ты правильно понимаешь кэширование на стороне mysql сервера.

он, если не ошибаюсь, тоже просто запоминает результат запроса.


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

Спустя 2 часа, 33 минуты, 38 секунд (29.06.2009 - 14:13) kirik написал(а):
Соглашусь с glock18, по поводу организации процедур.
vedmed а вообще сайт на какую посещаемость рассчитывается? Если не много, то можно и не заморачиваться наверное.. сделать одним запросом. Кстати на каком хостинге он будет работать?

Спустя 1 час, 53 минуты, 10 секунд (29.06.2009 - 16:06) vedmed написал(а):
Посещаемость 5к-6к. Хостинг не знаю, если честно.

Спустя 1 день, 19 часов, 24 минуты, 36 секунд (1.07.2009 - 11:31) vedmed написал(а):
qlock18, а не мог бы привести пример хранимой процедуры для моего случая, если не трудно? Не пробовал еще писать хранимые процедуры в MySQL.

Спустя 1 час, 3 минуты, 5 секунд (1.07.2009 - 12:34) glock18 написал(а):
vedmed
для твоего случая... вряд ли сейчас быстро соображу, потому что немного не в курсе что конкретно за задача. лучше объясню принцип.

Собственно, вступление маленькое:

Чтобы создать/изменить хранимую процедуру нужно выполнить следующее:
SQL
DELIMITER $$

DROP PROCEDURE IF EXISTS `your_db`.`your_sp` $$
CREATE PROCEDURE `your_sp`(IN param1_in VARCHAR(4096), IN param2_in INTEGER, OUT @param3_out INTEGER)
BEGIN # само тело процедуры
END $$

DELIMITER ;


про параметры советую на mysql.com почитать внимательней, потому что там нюансов много.

И советую разобраться "почему это дело работает" smile.gif Собственно, первая и последняя строки тебе должны быть исчерпывающим объяснением.

Далее, ты можешь объявлять переменные внутри процедуры так:

SQL
DECLARE current_index INTEGER DEFAULT 1;


или так:

SQL
SET @tagtype_id = in_tagtype_id;


Вторая строка по сути - присваивание. Дело в том, что переменные с @ не нужно объявлять специально с DECLARE.

Разница между этими двумя переменными в том, что переменные с @ можно использовать в подготовленных запросах. Может еще какие-то различия есть, я больше не знаю.

В процедурах можно вызывать любые функции mysql + использовать любые управляющие конструкция типа if, repeat (это цикл такой типа do-while) и while вроде тоже есть.

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

Например, такой кусок процедуры должен нормально работать:

SQL
SELECT COUNT(*) INTO my_count FROM `table` WHERE `type` = type_variable;

type_variable - это переменная, объявленная в DECLARE или переданная IN/INOUT параметром.

можно так с использованием подготовленного запроса (обеспечивает защиту от sql-инъекций):

SQL
PREPARE stmt_count FROM style='color:red'>' SELECT COUNT(*) INTO @my_count FROM `table` WHERE `type` = ?'; # подготавливаем запрос
EXECUTE stmt_count USING @type; # выполняем с подстановкой @type вместо ?


"вопросов" в запросе может быть много, тогда количество переменных после USING должно быть таким же, а следовать должны в таком же порядке, как и "вопросы" в запросе. Разумеется, в @type нужно перед этим положить что-то.

PS: че-т запрос execute у меня съелся сначала. все равно байда какая-то. В общем, вся хрень
SQL
style='color:red'>'

между FROM и SELECT - это одна одинарная кавычка smile.gif

Спустя 1 час, 7 минут, 48 секунд (1.07.2009 - 13:42) vedmed написал(а):
Ок.Спасибо.Буду думать!

Спустя 6 дней, 7 часов, 23 минуты, 24 секунды (7.07.2009 - 21:05) vedmed написал(а):
Доброго времени суток всем! Теперь столкнулся с проблемой оптимизации запросов. Вот такой запрос:
SQL
SELECT firm_w,COUNT(*) FROM watch_records GROUP BY firm_w
. Требуется получить в результате сколько часов каждой фирмы в базе, например: Полет-20, Слава-30 и т.д.
Создал индекс длиной в 30 символов по полю firm_w( тип поля TINYTEXT).
По EXPLAIN получается что при выполнении этого запроса не один индекс не используется: "possible key" - null, "key" - null, "key_length" - null, в итоге запрос выполняется 1,5 - 3 сек. Подскажите, пожалуйста, как можно его оптимизировать! Заранее большое спасибо!

Спустя 1 час, 28 минут, 34 секунды (7.07.2009 - 22:34) kirik написал(а):
vedmed
Дай дамп своей базы..

Спустя 9 часов, 18 минут, 47 секунд (8.07.2009 - 07:53) vedmed написал(а):
Вот, kirik, дамп таблицы:
SQL
DROP TABLE IF EXISTS `watch_records`;
CREATE TABLE `watch_records` (
`id_w` int(11) NOT NULL auto_increment,
`firm_w` tinytext NOT NULL,
`collect_w` tinytext,
`model_w` tinytext NOT NULL,
`modif_w` tinytext NOT NULL,
`ref_w` tinytext NOT NULL,
`mat_w1` tinytext,
`mat_w2` tinytext,
`mat_w3` tinytext,
`mat_bras` tinytext,
`size_w` tinytext,
`sex_w` tinytext,
`form_w` tinytext,
`color_body` tinytext,
`color_bez` tinytext,
`color_dial` tinytext,
`bras_w` tinytext,
`hasp_w` tinytext,
`mark_dial` tinytext,
`glass` tinytext,
`inc_body` tinytext,
`inc_body_mat` tinytext,
`inc_dial` tinytext,
`inc_dial_mat` tinytext,
`inc_bras` tinytext,
`inc_bras_mat` tinytext,
`country` tinytext,
`year_w` tinytext,
`view` tinyint(4) default NULL,
`des_body` mediumtext,
`mech_w` tinytext,
`sdes_w` mediumtext,
`fdes_w` longtext,
`ph1` tinytext,
`des_ph1` mediumtext,
`ph2` tinytext,
`des_ph2` mediumtext,
`ph3` tinytext,
`des_ph3` mediumtext,
`ph4` tinytext,
`des_ph4` mediumtext,
`ph5` tinytext,
`des_ph5` mediumtext,
`ph6` tinytext,
`des_ph6` mediumtext,
`ph7` tinytext,
`des_ph7` mediumtext,
`ph8` tinytext,
`des_ph8` mediumtext,
`ph9` tinytext,
`des_ph9` mediumtext,
`ph10` tinytext,
`des_ph10` mediumtext,
`popular_w` enum('1','0') default NULL,
`popular_date` date default NULL,
`not_fdes` enum('1','0') default NULL,
`cost_ok` enum('0','1') default NULL,
`check_red` enum('1','0') default NULL,
`not_client` enum('1','0') default NULL,
`print_cat` enum('1','0') default NULL,
`big_cat` enum('1','0') default NULL,
`mw_cat` enum('1','0') default NULL,
`shop_on` enum('On','Off') default 'On',
`data_reg` date default NULL,
`date_upd` date default NULL,
PRIMARY KEY (`id_w`),
KEY `id_w` (`id_w`),
KEY `firm_w` (`firm_w`(30))
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=cp1251;

Спустя 46 минут, 26 секунд (8.07.2009 - 08:39) kirik написал(а):
vedmed
Вместе с данными можешь дать? (тобишь полный дамп структура+данные)

Спустя 59 минут, 38 секунд (8.07.2009 - 09:39) vedmed написал(а):
Извини, kirik, вот данные к ней:
SQL
INSERT INTO `watch_records` VALUES ('1', 'Adidas', '- - - -', '1', '1', '1', 'Золото', 'Золото', 'Золото', 'Натуральная кожа', '6', '0', '0', 'Красный', 'Красный', 'Красный', '2', 'Бабочка', '0', '1', '1', 'Бриллианты', '1', 'Бриллианты', '2', 'Бриллианты', '- - - - ', '2008', '0', '', 'Механические', '', '', '39.jpg', '', '40.jpg', '', '41.jpg', '', '42.jpg', '', '39-prev.jpg', '', '39-prev.jpg', '', '40-prev.jpg', '', '41-prev.jpg', '', '42-prev.jpg', '', '39-prev-prev.jpg', '', '1', '2009-04-01', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-01', '2009-06-22');
INSERT INTO `watch_records` VALUES ('2', 'Adidas', '- - - -', 'Модель1', 'Модификация1', 'Реф1', 'Платина', 'Платина', 'Платина', 'Каучук', '0', '0', '0', 'Красный', 'Красный', 'Красный', '3', 'Бабочка', '2', '1', '1', 'Бриллианты', '1', 'Бриллианты', '1', 'Бриллианты', 'Россия', '2008', '0', '', 'Кварцевые', '', '', '40.jpg', '', '', '', '', '', '', '', '40-prev.jpg', '', '40-prev.jpg', '', '', '', '', '', '', '', '40-prev-prev.jpg', '', '1', '2009-04-27', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-04', '2009-04-12');
INSERT INTO `watch_records` VALUES ('3', 'Adidas', '- - - -', '123', '123', '123', 'Золото', 'Золото', 'Золото', 'Каучук', '4', '1', '0', 'Красный', 'Красный', 'Красный', '2', 'Бабочка', '0', '3', '0', 'Бриллианты', '0', 'Бриллианты', '0', 'Бриллианты', '- - - - ', '- - - -', '0', '', 'Механические', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '1', '2009-04-04', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-04', '2009-04-06');
INSERT INTO `watch_records` VALUES ('4', 'Adidas', '- - - -', '555', '555', '555', 'Золото', 'Золото', 'Золото', 'Натуральная кожа', '2', '2', '0', 'Красный', 'Красный', 'Красный', '0', 'Бабочка', '0', '2', '1', 'Бриллианты', '1', 'Бриллианты', '1', 'Бриллианты', 'Россия', '2009', '0', '', 'Механические', '', '', '39.jpg', '', '', '', '', '', '', '', '39-prev.jpg', '', '39-prev.jpg', '', '', '', '', '', '', '', '39-prev-prev.jpg', '', '1', '2009-04-04', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-04', '2009-06-18');
INSERT INTO `watch_records` VALUES ('5', 'Adidas', '- - - -', '777', '777', '777', 'Золото', 'Золото', 'Золото', 'Натуральная кожа', '3', '0', '0', '- - - -', '- - - -', 'Красный', '0', '- - - -', '0', '2', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', 'Кварцевые', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '0', '2009-04-09', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-09', '2009-04-11');
INSERT INTO `watch_records` VALUES ('6', 'Adidas', '- - - -', '999', '999', '999', 'Платина', 'Платина', '- - - -', '- - - -', '4', '0', '0', 'Красный', '- - - -', '- - - -', '2', 'Бабочка', '0', '1', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', 'Кварцевые', '', '', '40.jpg', '', '', '', '', '', '', '', '40-prev.jpg', '', '40-prev.jpg', '', '', '', '', '', '', '', '40-prev-prev.jpg', '', '0', '2009-04-09', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-09', '2009-04-18');
INSERT INTO `watch_records` VALUES ('7', 'Adidas', '- - - -', 'Мод111', 'Мод111', 'Реф111', 'Платина', 'Платина', '- - - -', '- - - -', '4', '0', '0', 'Красный', '- - - -', '- - - -', '2', 'Бабочка', '0', '1', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', 'Кварцевые', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '0', '2009-04-09', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-09', '2009-04-18');
INSERT INTO `watch_records` VALUES ('9', 'Atlantic', '- - - -', 'At2', 'At2', 'At2', 'Платина', 'Платина', 'Золото', 'Натуральная кожа', '3', '0', '0', 'Красный', 'Красный', 'Красный', '1', 'Бабочка', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '0', '2009-04-12', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-12', '2009-04-12');
INSERT INTO `watch_records` VALUES ('10', 'Adidas', '- - - -', 'At3', 'At3', 'At3', '- - - -', '- - - -', '- - - -', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '36.jpg', '', '', '', '', '', '', '', '36-prev.jpg', '', '36-prev.jpg', '', '', '', '', '', '', '', '36-prev-prev.jpg', '', '0', '2009-04-12', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-12', '2009-04-12');
INSERT INTO `watch_records` VALUES ('11', 'Atlantic', '- - - -', 'At4', 'At4', 'At4', '- - - -', '- - - -', '- - - -', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '0', '2009-04-12', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-04-12', '2009-04-12');
INSERT INTO `watch_records` VALUES ('21', 'Atlantic', '- - - -', '456', '456', '456', 'Золото', 'Золото', 'Золото', 'Каучук', '0', '0', '0', '- - - -', 'Красный', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', 'Механические', '', '', '36.jpg', '', '', '', '', '', '', '', '36-prev.jpg', '', '36-prev.jpg', '', '', '', '', '', '', '', '36-prev-prev.jpg', '', '0', '2009-05-02', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-05-02', '2009-05-02');
INSERT INTO `watch_records` VALUES ('22', 'Atlantic', '- - - -', '123 45 567', '234', '234 65 56 456 657 789', 'Золото', 'Золото', '- - - -', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', 'Кварцевые', '', '', '43.jpg', '', '', '', '', '', '', '', '43-prev.jpg', '', '43-prev.jpg', '', '', '', '', '', '', '', '43-prev-prev.jpg', '', '0', '2009-05-03', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-05-03', '2009-05-03');
INSERT INTO `watch_records` VALUES ('23', 'Adidas', '- - - -', '1999', '1999', '1999', '- - - -', '- - - -', '- - - -', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '38.jpg', '', '', '', '', '', '', '', '38-prev.jpg', '', '38-prev.jpg', '', '', '', '', '', '', '', '38-prev-prev.jpg', '', '0', '2009-05-06', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-05-06', '2009-05-06');
INSERT INTO `watch_records` VALUES ('29', 'Adidas', '- - - -', '00000000', '0000000000', '000000000', '- - - -', '- - - -', '- - - -', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '38.jpg', '', '39.jpg', '', '40.jpg', '', '41.jpg', '', '', '', '38-prev.jpg', '', '39-prev.jpg', '', '40-prev.jpg', '', '41-prev.jpg', '', 'Object id #27', '', '0', '2009-06-01', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-06-01', '2009-06-01');
INSERT INTO `watch_records` VALUES ('35', 'Adidas', '- - - -', 'fffffffffffffff', 'ffffffffffffff', 'fffffffffffffff', 'Платина', 'Платина', 'Платина', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '40.jpg', '', '41.jpg', '', '43.jpg', '', '37.jpg', '', '36.jpg', '', '40-prev.jpg', '', '41-prev.jpg', '', '43-prev.jpg', '', '37-prev.jpg', '', '36-prev.jpg', '', '0', '2009-06-01', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-06-01', '2009-06-01');
INSERT INTO `watch_records` VALUES ('36', 'Adidas', '- - - -', 'zzzzzzzzz', 'zzzzzzzzzzzz', 'zzzzzzzzz', 'Платина', 'Платина', 'Платина', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '43.jpg', '', '42.jpg', '', '41.jpg', '', '', '', '', '', '43-prev.jpg', '', '42-prev.jpg', '', '41-prev.jpg', '', '', '', '', '', '0', '2009-06-01', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-06-01', '2009-06-01');
INSERT INTO `watch_records` VALUES ('38', 'Atlantic', '- - - -', 'jjj', 'jjj', 'jjj', 'Золото', 'Золото', 'Золото', '- - - -', '0', '0', '0', '- - - -', '- - - -', '- - - -', '0', '- - - -', '0', '0', '0', '- - - - ', '0', '- - - - ', '0', '- - - - ', '- - - - ', '- - - -', '0', '', '- - - -', '', '', '43.jpg', '', '38.jpg', '', '41.jpg', '', '', '', '', '', '43-prev.jpg', '', '38-prev.jpg', '', '41-prev.jpg', '', '', '', '', '', '0', '2009-06-01', '0', '0', '0', '0', '0', '0', '0', 'On', '2009-06-01', '2009-06-01');

Спустя 14 минут, 31 секунда (8.07.2009 - 09:53) kirik написал(а):
vedmed
я щас спать, завтра погляжу, если не поможет никто до меня smile.gif

Спустя 22 минуты, 54 секунды (8.07.2009 - 10:16) vedmed написал(а):
Хорошо. Буду ждать.Спасибо.

Спустя 9 часов, 31 минута, 3 секунды (8.07.2009 - 19:47) kirik написал(а):
vedmed
Уу, друг! Да с твоей таблицей тот запрос, который мы написали просто убъет сервер.
Зачем ты везде где только можно используешь tinytext? Многие поля не не знаю для чего нужны, но почему year_w тоже tinytext?

Держи новый дамп, теперь с группировкой по индексу. Что сделал -
1. удалил индекс по полю id_w, потому что оно и так праймари
2. поставил тип поля firm_w как varchar(50) (50 символов вполне должно хватить для названия часов)
3.
SQL
CHECK TABLE `watch_records`;
OPTIMIZE TABLE `watch_records`;

4. вроде бы ничего не забыл smile.gif


Свернутый текст
SQL
CREATE TABLE IF NOT EXISTS `watch_records` (
`id_w` int(11) NOT NULL auto_increment,
`firm_w` varchar(50) NOT NULL,
`collect_w` tinytext,
`model_w` tinytext NOT NULL,
`modif_w` tinytext NOT NULL,
`ref_w` tinytext NOT NULL,
`mat_w1` tinytext,
`mat_w2` tinytext,
`mat_w3` tinytext,
`mat_bras` tinytext,
`size_w` tinytext,
`sex_w` tinytext,
`form_w` tinytext,
`color_body` tinytext,
`color_bez` tinytext,
`color_dial` tinytext,
`bras_w` tinytext,
`hasp_w` tinytext,
`mark_dial` tinytext,
`glass` tinytext,
`inc_body` tinytext,
`inc_body_mat` tinytext,
`inc_dial` tinytext,
`inc_dial_mat` tinytext,
`inc_bras` tinytext,
`inc_bras_mat` tinytext,
`country` tinytext,
`year_w` tinytext,
`view` tinyint(4) default NULL,
`des_body` mediumtext,
`mech_w` tinytext,
`sdes_w` mediumtext,
`fdes_w` longtext,
`ph1` tinytext,
`des_ph1` mediumtext,
`ph2` tinytext,
`des_ph2` mediumtext,
`ph3` tinytext,
`des_ph3` mediumtext,
`ph4` tinytext,
`des_ph4` mediumtext,
`ph5` tinytext,
`des_ph5` mediumtext,
`ph6` tinytext,
`des_ph6` mediumtext,
`ph7` tinytext,
`des_ph7` mediumtext,
`ph8` tinytext,
`des_ph8` mediumtext,
`ph9` tinytext,
`des_ph9` mediumtext,
`ph10` tinytext,
`des_ph10` mediumtext,
`popular_w` enum('1','0') default NULL,
`popular_date` date default NULL,
`not_fdes` enum('1','0') default NULL,
`cost_ok` enum('0','1') default NULL,
`check_red` enum('1','0') default NULL,
`not_client` enum('1','0') default NULL,
`print_cat` enum('1','0') default NULL,
`big_cat` enum('1','0') default NULL,
`mw_cat` enum('1','0') default NULL,
`shop_on` enum('On','Off') default 'On',
`data_reg` date default NULL,
`date_upd` date default NULL,
PRIMARY KEY (`id_w`),
KEY `firm_w` (`firm_w`)
) ENGINE=InnoDB DEFAULT CHARSET=cp1251 AUTO_INCREMENT=39 ;


Очень советую пересмотреть остальные типы полей. (если поле числовое и не может быть отрицательно, то юзай атрибут UNSIGNED (также обрати внимание на максимальные/минимальные ограничения для типа и диапазоны), если текстовое меньше 255 символов, то тип varchar(n), если известной длины меньше 255 символов, то тип char(n)...)

Спустя 14 часов, 3 минуты, 46 секунд (9.07.2009 - 09:51) Guest написал(а):
Огромное спасибо,kirik, за помощь и за критику. Люблю хорошую критику, так как она только помогает, в следующий раз буду умнее. Просто это мой первый проект и на нем я учусь sad.gif
У меня такие вопросы:
1. CHECK TABLE `watch_records`;
OPTIMIZE TABLE `watch_records`; - это для чего нужно?
2. "Да с твоей таблицей тот запрос, который мы написали просто убъет сервер." - запрос подсчета количества часов? А как же быть?
3. Т.е. по возможности нужно избегать использования типа поля TINYTEXT?

Спустя 2 минуты, 6 секунд (9.07.2009 - 09:53) vedmed написал(а):
Млин, забыл авторизироваться...Это я написал.

Спустя 10 часов, 32 минуты, 12 секунд (9.07.2009 - 20:25) kirik написал(а):
Цитата (Guest @ 9.07.2009 - 01:51)
1. CHECK TABLE `watch_records`;
OPTIMIZE TABLE `watch_records`; - это для чего нужно?

CHECK TABLE - проверяет таблицу на наличие ошибок, OPTIMIZE TABLE - оптимизирует таблицу, после того как мы немного изменили структуру, mysql нужно сказать чтобы он заново перераспределил используемое место на диске.

Цитата (Guest @ 9.07.2009 - 01:51)
2. "Да с твоей таблицей тот запрос, который мы написали просто убъет сервер." - запрос подсчета количества часов? А как же быть?

Использовать те типы полей, которые реально нужны, и не больше.
Для примера:
Допустим у нас есть таблица
Код
id | username | register_date | user_ip | active | info

id - содержит уникальный номер юзера, тоесть поле числовое. Можно не думая ткнуть его как int, а можно посмотреть сюда, увидеть что максимальное число у int при использовании со знаком "2147483647", думаем еще... Мы знаем, что отрицателным id быть не может, значит нам можно поставить поле в int unsigned, смотрим еще раз табличку, теперь максимум юзеров у нас может быть "4294967295". Таак.. За пару сотен лет может быть наш сайт насобирает столько пользователей smile.gif Нам нужно меньшее число, чтоб не мучать mysql. "16777215" вполне подойдет. 16.7 миллионов пользователей у нас будет очень не скоро, поэтому будем юзать mediumint unsigned.

username будет содержать имя юзера, которое будет текстовое и не больше 20 символов. Также можно ткнуть text, который вмещает 64кб данных и вспомнить про это только тогда, когда БДсервер упадет. А можно сделать его например varchar(20) и все будет как нужно.

register_date дата регистрации. Можно конечно использовать тип int unsigned, чтобы хранить там время в unix timestamp, но mysql будет гораздо удобнее оперировать с датами, если они будут записаны как даты (а не как какое-то кол-во секунд прошедшее с 70-го). Нам нужно время и число. datetime вполне подойдет!

user_ip.. IP у нас может быть вида 255.255.255.255, присутствуют числа и точки. Что нужно? Конечно поле varchar(15)! Хер!Нет! Нужно int unsigned! Спросишь: "а как-же хранить точки?". Отвечу - есть специальные функции в mysql для работы с IP адресами: INET_ATON() [функция переводит IP в число] и INET_NTOA() [функция переводит число в IP].

active - статус юзера. Может быть "активный" - 1, "не активный" - 0. Для этого тип bool нужен, так?

И последнее info, будет держать некоторую текстовую инфу о юзере. Описание, биографию, или просто спам. В любом случая varchar(255) как и tinytext не подойдут. Остается тип text. Его и используем.

Полезные ссылки: типы данных, советы по оптимизации 1, советы по оптимизации 2 и еще есть google smile.gif

Спустя 12 часов, 53 минуты, 25 секунд (10.07.2009 - 09:19) vedmed написал(а):
user posted image Круто! Спасибо огронмое, kirik, за такое полное описание процесса. Мерси. В след раз буду думать по-другому, с той точки зрения, что ты расписал. Надеюсь не очень тебя запарил своими проблемами?

Спустя 23 минуты, 48 секунд (10.07.2009 - 09:42) kirik написал(а):
Цитата (vedmed @ 10.07.2009 - 01:19)
В след раз буду думать по-другому

Не.. не нужно "в следующий раз". Переделай эту табличку.

Цитата (vedmed @ 10.07.2009 - 01:19)
Надеюсь не очень тебя запарил своими проблемами?

Интересно отвечать на непростые вопросы, хоть думать начинаю smile.gif

Спустя 1 час, 42 минуты, 10 секунд (10.07.2009 - 11:25) vedmed написал(а):
Переделаю, конечно. Имел ввиду, что в следующий раз буду сразу думать по-другому.
Если интересно, то могу скинуть тебе ссылочку на сайт, сможешь посмотреть что делаю, высказать свое мнение.

Спустя 4 часа, 41 минута, 50 секунд (10.07.2009 - 16:06) kirik написал(а):
vedmed
давай, интересно smile.gif

Спустя 2 часа, 2 минуты, 23 секунды (10.07.2009 - 18:09) vedmed написал(а):
Отправил в личку.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.