Метод створення підзапиту за допомогою JDatabase


31

На сторінці http://docs.joomla.org/Selecting_data_using_JDatabase не існує документально підтвердженого методу написання підзапиту за допомогою JDatabase.

https://gist.github.com/gunjanpatel/8663333 пояснює один із способів досягти цього за допомогою (пропущено кілька біт):

$subQuery = $db->getQuery(true);
$query    = $db->getQuery(true);

// Create the base subQuery select statement.
$subQuery->select('*')
    ->from($db->quoteName('#__sub_table'))
    ->where($db->quoteName('subTest') . ' = ' . $db->quote('1'));

// Create the base select statement.
$query->select('*')
    ->from($db->quoteName('#__table'))
    ->where($db->quoteName('state') . ' = ' . $db->quote('1'))
    ->where($db->quoteName('subCheckIn') . ' IN (' . $subQuery->__toString() . ')')
    ->order($db->quoteName('ordering') . ' ASC');

// Set the query and load the result.
$db->setQuery($query);

Це здається хорошим, правдоподібним підходом, але чи є кращий?


4
Ви можете опустити виклик toString () у $ subQuery. Joomla! автоматично обробляє це за вас. Крім цього, я використовую цей самий метод, і він добре працює для мене.
Захарій Дрейпер

Це також той самий метод, який ми використовуємо в com_content в core github.com/joomla/joomla-cms/blob/staging/components/…
Джордж Вілсон,

@ZacharyDraper цікаво. Чи можете ви показати код, який за це відповідає?
Дмитро Рекун

3
@ZacharyDraper: PHP (а не Joomla! Per se) обробляє це для вас ( __toString()) - це "магічний" метод.
MrWhite

Так, дякую w3d.
Захарій Дрейпер

Відповіді:


16

Так, наскільки я переживаю, те, як ви побудували підзапит, - це той, який прийняв більшість розробників розширень Joomla.

Я використовую той самий метод для деяких своїх розширень та спеціальних розширень, зроблених для клієнтів.

Існує не "офіційний" спосіб зробити це, але робити це так, як ви показали, дозволяє використовувати конструктор запитів і все ще зберігати хорошу кількість читабельності


10

AFAIK не існує вбудованого способу робити прості підзапити, що, ймовірно, є недоліком у системі, і його слід виправити за допомогою PR.

Однак я не бачу жодної проблеми з вашим прикладом - здається досить розумною.

~~~

Ось приклад у відповідь на коментар @ DavidFritsch нижче. Чим більше я думаю про це, тим краще мені подобається більш простий підхід, відображений в ОП. Ясніше, що відбувається.

$query = $this->db->getQuery(true)
  ->select('a.*')
  ->subQuery()
    ->select('b.*')
    ->from('#__table_b AS b')
    ->as('subQueryResult')
  ->endSubQuery()
  ->from('#__table_a AS a');

1
Чи маєте ви якесь уявлення, як це могло б змусити працювати? Я намагаюся уявити формат, який ви використовували б для того, щоб зробити цю роботу над одним об’єктом запиту, і нічого насправді не відчувається легше, ніж цей метод.
Девід Фрітш

1
Можливо, варто створити subQuerySelectметод, в якому він дозволяє зробити це трохи «чисто». Я відредагую свою відповідь, щоб надати та приклад.
Дон Гілберт

Я хотів би побачити, що в Joomla
fruppel

3

Існує також спосіб виконання запитів, які містять підзапити за допомогою API платформи Joomla. Основна ідея використання підзапитів базується на gunjanpatel .

Ось приклад для виконання запитів у вкладених моделях набору :

SQL-запит:

-- Find the Immediate Subordinates of a Node
SELECT node.title, (COUNT(parent.id) - (sub_tree.depth + 1)) AS depth
FROM lubd3_usergroups AS node,
        lubd3_usergroups AS parent,
        lubd3_usergroups AS sub_parent,
        (
                SELECT node.id, (COUNT(parent.id) - 1) AS depth
                FROM lubd3_usergroups AS node,
                        lubd3_usergroups AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.id = 1
                GROUP BY node.id
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.id = sub_tree.id
GROUP BY node.id
-- not showing the parent node
HAVING depth = 1
-- showing the parent node
-- HAVING depth <= 1
ORDER BY node.lft;

і перетворений запит, який повинен виконувати Joomla:

// Create the subQuery select statement.
// Nested Set Queries: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
// CROSS JOIN: http://www.informit.com/articles/article.aspx?p=30875&seqNum=5
$subQuery->select(array('node.id', '(COUNT(parent.id) - 1) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt') . ' AND ' . $db->quoteName('node.id') . ' = ' . $db->quote('1'))
    ->group($db->quoteName('node.id'))
    ->order($db->quoteName('node.lft'));

// Create the base select statement.
$query->select(array('node.title', '(COUNT(parent.id) - (sub_tree.depth + 1)) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->join('CROSS', $db->quoteName('#__usergroups', 'sub_parent'))
    ->join('CROSS', '(' . $subQuery .') AS sub_tree')
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt')
    . ' AND ' . $db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('sub_parent.lft') . ' AND ' . $db->quoteName('sub_parent.rgt')
    . ' AND ' . $db->quoteName('sub_parent.id') . ' = ' . $db->quoteName('sub_tree.id'))
    ->group($db->quoteName('node.id'))
    ->having($db->quoteName('depth') . ' = ' . $db->quote('1'))
    ->order($db->quoteName('node.lft'));

// Set the query and load the result.
$db->setQuery($query);
$rowList = $db->loadAssocList();

echo "<pre>";
print_r($rowList);
echo "</pre>";

1
Виглядає добре, але абсолютно так само, як у прикладі ОП: Спершу зробіть підзапит і використовуйте його в головному запиті. Питання було, чи є кращий шлях.
fruppel

1

Я запропоную свою версію фрагмента, а потім поясню моє обгрунтування і включаю цитати з посібника зі стандартів кодування Joomla (який буде відформатований блоком цитат).

$subquery = $db->getQuery(true)
    ->select("checkin")
    ->from("#__sub_table")
    ->where("subTest = 1");

$query = $db->getQuery(true)
    ->select("*")
    ->from("#__table")
    ->where([
        "state = 1",
        "subCheckIn IN ({$subQuery})"
    ])
    ->order("ordering");

$db->setQuery($query);

Використовуйте ланцюг запитів для підключення декількох методів запитів один за одним, при цьому кожен метод повертає об'єкт, який може підтримувати наступний метод. Це покращує читабельність та спрощує отриманий код.

  • Я спочатку пишу найпотаємніші запити і переходжу до самого зовнішнього запиту. Це дозволяє мені ланцюжок всіх методів побудови запитів безпосередньо до getQuery()методу. Ефективно ім'я змінної пишеться лише один раз під час побудови індивідуального запиту.
    Ось приголомшливий приклад деяких гнучких запитів (коли я думав, що мило вишикувати ланцюгові стрілки).

  • Я намагаюся уникати декількох select()та / або where()дзвінків у межах одного запиту, оскільки я бачив, що це призводить до плутанини менш досвідчених розробників . Оскільки ці методи приймають масиви, я вважаю, що їх можна читати і краще кодувати практикою їх використання.

  • і нарешті найсуперечливіша тема ...

    Імена таблиць та назви стовпців таблиці завжди повинні бути додані до методу quoName (), щоб уникнути імені таблиці та стовпців таблиці. Значення полів, перевірених у запиті, завжди повинні міститись у методі quo (), щоб уникнути значення перед передачею його до бази даних. Значення полі цілого числа, що перевіряються в запиті, також повинні бути відведені типу (int).

    Я дуже конфліктую з цього приводу. Коли я вперше прийшов до Joomla минулого року, я подумав, що я не збираюся здійснювати марні дзвінки (ніякої користі для стабільності, безпеки, читабельності запиту) щодо статичних значень! Тим НЕ менше, мій роботодавцю подобається ідея Зайвих ліній Joomla, і я повинен визнати , що я , як правило, дуже високий бал за для правил, тому я поливанням моїх запитів з quote(), (int)і quoteName()що також означає купку конкатенації (все правильно розташовані). Кінцеві результати моєї роботи - жахливо роздуті блоки запитів, які навіть у мене важко підглядають. Найгірші / найдовші рядки, які не піддаються вертикальній join()стековці, - це дзвінки через ім'я таблиці, псевдонім ON, а потім одне або кілька умов, які можуть вимагати або не вимагати цитування.Я можу оцінити, що ця політика реалізується з урахуванням безпеки для початківців розробників, але я впевнений, що хотілося б, якби ця політика була якось загартована з тим, що не всі кодери Joomla є необізнаними копієрами. Я маю на увазі, погляньте на те, як чистий і короткий вигляд виглядає код без зайвих дзвінків.

  • Що стосується прибирання:

    • Я майже ніколи не використовую *в своїх SELECT застереженнях
    • Я ніколи не дзвоню __toString()
    • Я не цитую цілі числа, я ставлю їх як цілі числа
    • Я не пишу, ASCтому що це напрямок сортування за замовчуванням
    • Я докладаю всіх зусиль, щоб не використовувати ключові слова mysql під час створення нових імен таблиць та імен стовпців
    • Що стосується особистих уподобань, я схильний використовувати подвійне цитування строкових аргументів свого методу, щоб підтримувати рівномірність, відрізняти від одного цитування mysql, і щоб я міг насолоджуватися змінною інтерполяцією, яку я типово пишу " складним синтаксисом ".
    • Я використовую інформативні назви змінних та коментарі, щоб допомогти в читанні моїх вкладених запитів та мого коду взагалі
    • Я перевіряю свій код до того, як він покине мою опіку
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.