SQL-запрос на основе Drupal через PHP: количество t1 присоединяется к t2

#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/>');
    }
  }
 // }
}