#php #mysql #drupal #join
#php #mysql #drupal #Присоединиться
Вопрос:
У меня есть две таблицы, подобные таким:
table {node}
`nid`, `uid`, `type`
1 1 basketball
2 1 basketball
3 1 football
4 2 football
5 2 basketball
table {strato_ticket}
`tid`, `author_uid`, `purpose`, `active`
1 1 'Ticket to a basketball game' TRUE
2 1 'Ticket to a football game' TRUE
3 2 'Ticket to a football game' FALSE
Я хотел бы сгенерировать отчет, который подсчитывает количество узлов каждого типа, а затем подсчитывает количество активных заявок, которые каждый пользователь связал с этим типом узла.
Мое решение использует комбинацию SQL и PHP: У меня есть цикл PHP для каждого интересующего меня типа узла, который упрощает SQL-запрос и переводит из «типа» в «назначение», например
$node_types = array('basketball', 'football');
foreach($node_types as $node){
switch($type){
case 'basketball':
$purpose = array('Ticket to a basketball node');
break;
case 'football':
$purpose = array('Ticket to a football game');
break;
}
$where = " WHERE ({strato_ticket}.`purpose` = '"
.implode("' OR {strato_ticket}.`purpose` = '",$purpose)."')";
Наконец, у меня есть проблемное место, SQL-запрос. Когда я просто считал узлы, принадлежащие каждому пользователю, это работало нормально:
$query = "
SELECT uid, count( * ) AS nodes_owned
FROM {node} WHERE `type` = '$type'
GROUP BY uid ORDER BY nodes_owned DESC
";
$query = db_query($query);
output:
Now displaying info for basketball.
uid nodes_owned
1 2
2 1
Now displaying info for football.
uid nodes_owned
1 1
2 1
Но теперь, когда мне нужно выполнить запрос к другой таблице, strato_ticket, все усложняется, и мой запрос возвращает FALSE без выдачи ошибки (я думаю).
$query = "
SELECT count(*) as tickets
FROM {strato_ticket} INNER JOIN (
SELECT node.uid, count( * ) AS nodes_owned
FROM {node} WHERE `type` = '$type'
GROUP BY uid
) AS {nodecount}
ON {strato_ticket}.`author_uid` = {nodecount}.`uid`
$where
GROUP BY nodecount.uid ORDER BY nodecount.nodes_owned DESC
";
$query = db_query($query);
Я не очень хорошо разбираюсь в SQL и не совсем уверен, как он сломан. Не могла бы ты немного помочь?
В идеале хотелось бы видеть
uid nodes_owned tickets
//basketball
1 2 1
2 1 0
//football
1 1 1
2 1 0
Комментарии:
1. Я бы посоветовал протестировать ваш запрос в консоли MySQL (где ошибки будут вам сразу очевидны), а затем просмотреть документы Drupal для получения рекомендаций по правильному написанию вашего SQL для db_query(). Я не думаю, что вам нужны завитушки вокруг {nodecount} выше.
2. Для наилучшей практики работы с БД в Drupal используйте заполнители БД (например, %s вместо $type и перепишите вашу конструкцию $ where, чтобы сделать то же самое).
3. Кроме того, не используйте `, который специфичен для mysql и не нужен, если только вы не используете зарезервированные слова для своих столбцов. Использование псевдонимов также должно упростить задачу:
{strato_ticket} st
а затем простоst.autor_uid
. И, как уже сказал Крис, используйте заполнители %s (важно для безопасности! не просто лучшая практика) и {} необходимы только для таблиц, поддерживаемых Drupal (например, если вы используете префикс имени базы данных). Не используйте его для псевдонимов или внешних таблиц.4. Понятия не имею, что вы имеете в виду,
select type from node limit 10;
работает совершенно нормально. Его также нет в списке на dev.mysql.com/doc/refman/5.5/en/reserved-words.html . Он указан на postgresql.org/docs/7.3/static/sql-keywords-appendix.html но как не зарезервированное слово. И даже тогда alias.column, имхо, должен работать нормально. Псевдонимирование имен таблиц не обязательно, но очень распространено в Drupal, именно потому, что в противном случае вы получаете очень длинные идентификаторы столбцов, как в вашем коде.5. Кроме того, я предлагаю использовать заполнители также для подобных случаев. Фактически, вы должны использовать db_placeholders и условие IN, подобное этому:
st.purpose IN (" . db_placeholders($purpose, 'varchar') . '")
. а затем передать $purpose в качестве аргументов. Снова намного короче, чем ваш код, и в Drupal 7 вам нужен только один заполнитель:st.purpose IN (:purposes)
.
Ответ №1:
Помимо заполнителей, к которым я смогу вернуться позже, я думаю, что это решает проблему.
$form = array();
$node_types = array('basketball','football');
// if($user->uid == 1){
$form[$type][] = array('#value'=>"Showing how many of each node type each user owns.".'<br/>');
foreach($node_types as $type){
// Count the number of nodes each user owns of $type.
$form[$type][] = array('#value'=>"Now displaying info for $type".'s. <br/>');
switch($type){
case 'basketball':
$purpose = array('ticket to a basketball game', 'basketball');
break;
case 'football':
$purpose = array('ticket to a football game');
break;
}
$purpose = implode("', '", $purpose);
//@todo : Make a temporary table to query against so I'm not hitting node table multiple times.
$ticketquery = "
SELECT author_uid, purpose, COUNT( * ) AS invitees_accepted
FROM {strato_ticket}
WHERE purpose IN ('$purpose')
GROUP BY author_uid, `purpose`
";
$nodequery = "
SELECT node.uid, count( * ) AS nodes_owned, type
FROM {node}
WHERE `type` IN ('$type')
GROUP BY uid, type";
$query = "
SELECT * FROM
($nodequery) AS nt
JOIN
($ticketquery) AS tt
ON nt.uid = tt.author_uid
GROUP BY nt.uid ORDER BY nt.nodes_owned DESC
";
drupal_set_message('Query is <br/>'.$query);
//return;
$query = db_query($query);
$first = true;
while ($rec = db_fetch_object($query)){
if($first){
$form[$type][] = array('#value'=>"And the winner is: ".print_r($rec, true).'<br/>');
$first = false;
}
else {
$form[$type][] = array('#value'=>print_r($rec, true).'<br/>');
}
}
// }
}