[sql] Выбрать строки по нескольким критериям в 2 колонках

Anton456

Задача похожа на эту.
Только структура таблицы такая.

















user_id prop_name prop_value

1
ancestry Germany

1
age 27

2
ancestry England

3
age 35


Ну и в итоге надо запилить сервис, который бы по входящему массиву name-value генерил SELECT, который бы возвращал удовлетворяющие всем условиям из массива name-value user_id.
Разворачивать свойства в колонки не хочется, потому что во-первых их список не окончательный и может меняться в обе стороны. Во-вторых (ну это мелочь, да, можно и null'ами забить) ни одно из свойств не является обязательным.
Для задачи выбрать всех пользователей 27 лет, родившихся в Германии, на вход сервиса приходит arr[]: arr[0].name = 'ancestry', arr[0].value = 'Germany', arr[1].name = 'age', arr[1].value = '27')
в принципе, наверное подходит такое решение (допиленный ответ с SO):
SELECT DISTINCT (user_id)
FROM user
WRE user.user_id In (select user_id from user where prop_name = 'ancestry' and prop_value = 'Germany')
    And user.user_id In (select user_id from user where prop_name = 'age ' and prop_value = '27')

Его по крайней мере можно слепить автоматически легко, но это вложенные запросы и выглядит как гавно.
В принципе, вся задача выглядит как гавно, конечно.
Может у неё есть какое-то очевидное и простое решение? :confused:
Если что, oracle.

SergeRRRRRR

а где в запросах prop_name и prop_value? И что такое map? ужасное описание.

Anton456

переписал

IG_rok777

SELECT user_id FROM
(SELECT user_id, COUNT(prop_name) AS num
FROM user
WRE (prop_name = 'ancestry' and prop_value = 'Germany') or (prop_name = 'age ' and prop_value = '27')
GROUP BY user_id)
WRE num = 2

В синтаксисе может где-то ошибся, руками не пишу.
Так не годится?

marat7256

Мне кажется правильно делать select из selectа. В твоем случае ты трижды парсиш таблицу полностью (если не считать оптимизаций, которые сделает сам оракл в плане запроса). Например:
 
SELECT DISTINCT (user_id)
FROM
(select * from
(select * from user where prop_name = 'age ' and prop_value = '27')
where prop_name = 'ancestry' and prop_value = 'Germany')

В синтаксисе мог налажать.

SergeRRRRRR

 
select * from ( select * from user pivot(max(prop_value) for prop_name in ('age' age,'ancestry' ancestry ) t
where t.age=27 and and t.ancestry= 'Germany'
 

Upd. не подходит под условие не полного списка, можно сделать и с полным списком, но через xml, не думаю, что будет быстрее.

SergeRRRRRR

Так вроде полностью универсальный запрос без дальнейших правок в случае новых prop_name. Но массив придется перегнать либо в какую-нить table type или xml.
 
 

select u.user_id from user u
join xmltable('for $root in $data/data/row return $root'
passing xmltype('<data><row><name>ancestry</name><value>Ger</value></row><row><name>age</name><value>27</value></row></data>') as "data" columns
name varchar2(512 char) path 'name',
value varchar2(512 char) path 'value') p
on( p.name=u.prop_name and p.value=u.prop_value)
group by u.user_id
having count(*) >= 2

Anton456

Скорее всего я хуево объяснил.
Сервис, который будет готовить select написан на js. То есть ему на вход подаётся массив name-value, он выплёвывает селект.
То есть вариант решает задачу. Твой вариант тоже решает, но сложнее.
Правильно я понимаю, что на нескольких десятках тысяч записей все запросы должны шустро отрабатывать?

SergeRRRRRR

вариант Ramir'a не подойдет, если появится не два, а 3, 4.. условия одновременно - придется каждый раз редактировать запрос.
Про js не понял, обычно js просто отсылает данные на интерфейс, с интерфейса идут в БД, и потом js обрабатывает полученные данные.
На счет шустрости все индивидуально.

Anton456

Так сервис на js (серверный) примерно такой (на вход принимает arr[])
 
  
var sql = "SELECT user_id FROM (SELECT user_id, COUNT(prop_name) AS num FROM user WRE ";
for (var i = 0; i < arr.length; i++) {
sql += ''(prop_name = " + arr[i].name + " and prop_value = " arr[i].value + ") OR";
}
sql += " GROUP BY user_id) WRE num = " + arr.length;
return sql;

SergeRRRRRR

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

IG_rok777

Так запрос в любом случае нужно редактировать. Несложно количество параметров подставить.

Alena_08_11

Вот ещё вариант

select
t1.user_id
from
TABLE_NAME t1
inner join (
select
prop_name = 'ancestry'
, prop_value = 'Germany'
from
dual
union all
select
prop_name = 'age'
, prop_value = 27
from
dual
) t2
on 1 = 1 and t1.prop_name = t2.prop_name and t1.prop_value = t2.prop_value
group by
t1.user_id
having
count(*) = 2 -- количество условий

Всё что в скобках - надо сгенерить, вместо двойки тоже подставить.
Предварительно убедиться, что в таблице нет одинаковых строк. (2 раза age=27 для одного и того же user_id)
ps. В каракулях же так делается селект из ниоткуда ?

SergeRRRRRR

в запросе с xml как я написал ничего править не надо, единственное - в условии having подставить переменную = кол-ву переданных параметров.
ну и для
Предварительно убедиться, что в таблице нет одинаковых строк. (2 раза age=27 для одного и того же user_id)

я бы уник ключ на все три столбца повесил бы, наверное.

Alena_08_11

Да, сорри, ща вглянулся и понял что написал то же самое.
Оставить комментарий
Имя или ник:
Комментарий: