Рекурсивный вызов Родителя/Ребенка SQL или объединение?

Наконец, я решил это !!!

SET @BALANCE:= 0;
SELECT A.*, @balance:=@balance + IFNULL(A.Q_IN, 0) - IFNULL(A.Q_OUT, 0)
from
(
(SELECT 'Old quantity' AS OPERATION, ID, OLD_QTY AS Q_IN, NULL AS Q_OUT FROM t_PRODS)
union all
(SELECT PROVIDER_NAME, PRODUCT_ID, QTY, NULL FROM t_INS)
union all
(SELECT 'Old quantity' AS OPERATION, ID, OLD_QTY AS Q_IN, NULL AS Q_OUT FROM t_PRODS)
) AS A
5
задан cletus 26 January 2009 в 02:22
поделиться

4 ответа

Хорошо, по-видимому, на основе upvotes для другого ответа, этому нужно дальнейшее объяснение. Пример (сделанный с MySQL, потому что у меня есть он удобный, но принцип универсален на любой диалект SQL):

CREATE TABLE Blah (
  ID INT PRIMARY KEY,
  SomeText VARCHAR(30),
  ParentID INT
)

INSERT INTO Blah VALUES (1, 'One', 0);
INSERT INTO Blah VALUES (2, 'Two', 0);
INSERT INTO Blah VALUES (3, 'Three', 1);
INSERT INTO Blah VALUES (4, 'Four', 1);
INSERT INTO Blah VALUES (5, 'Five', 4);

Оставленная версия соединения:

SELECT a.ID, a.SomeText, COUNT(1)
FROM Blah a
JOIN Blah b ON a.ID= b.ParentID
GROUP BY a.ID, a.SomeText

Неправильно. Игнорирует регистр без детей.

Оставленное внешнее объединение:

SELECT a.ID, a.SomeText, COUNT(1)
FROM Blah a
LEFT OUTER JOIN Blah b ON a.ID= b.ParentID
GROUP BY a.ID, a.SomeText

Неправильно и причина, почему является несколько тонким. COUNT(1) количества NULL строки, тогда как COUNT(b.ID) не делает. Таким образом, вышеупомянутое является неправильным, но это корректно:

SELECT a.ID, a.SomeText, COUNT(b.ID)
FROM Blah a
LEFT OUTER JOIN Blah b ON a.ID= b.ParentID
GROUP BY a.ID, a.SomeText

Связанный подзапрос:

SELECT ID, SomeText, (SELECT COUNT(1) FROM Blah WHERE ParentID= a.ID) ChildCount
FROM Blah a

Также корректный.

Хорошо, поэтому чтобы использовать? Планы только говорят Вам так. Проблемой подзапросов по сравнению с лево-соединениями является старая и нет никакого четкого ответа, не сравнивая ее. Таким образом, нам нужны некоторые данные:

<?php
ini_set('max_execution_time', 180);

$start = microtime(true);

echo "<pre>\n";

mysql_connect('localhost', 'scratch', 'scratch');
if (mysql_error()) {
    echo mysql_error();
    exit();
}
mysql_select_db('scratch');
if (mysql_error()) {
    echo mysql_error();
    exit();
}

$count = 0;
$limit = 1000000;
$this_level = array(0);
$next_level = array();

while ($count < $limit) {
    foreach ($this_level as $parent) {
        $child_count = rand(0, 3);
        for ($i=0; $i<$child_count; $i++) {
            $count++;
            query("INSERT INTO Blah (ID, SomeText, ParentID) VALUES ($count, 'Text $count', $parent)");
            $next_level[] = $count;
        }
    }
    $this_level = $next_level;
    $next_level = array();
}

$stop = microtime(true);
$duration = $stop - $start;
$inserttime = $duration / $count;

echo "$count users added.\n";
echo "Program ran for $duration seconds.\n";
echo "Insert time $inserttime seconds.\n";
echo "</pre>\n";

function query($query) {
    mysql_query($query);
    if (mysql_error()) {
        echo mysql_error();
        exit();
    }
}
?>

У меня закончилась память (32M) во время этого выполнения поэтому, только закончился с 876 109 записями, но эй это сделает. Позже, когда я тестирую Oracle Server и SQL Server, я беру тот же самый набор данных и импортирую его в Oracle XE and SQL Server Express 2005.

Теперь другой плакат поднял вопрос моего использования обертки количества вокруг запросов. Он правильно указал, что оптимизатор не может выполнить подзапросы в этом случае. MySQL, кажется, не настолько умен. Oracle. SQL Server, кажется, также.

Таким образом, я приведу два данные для каждой комбинации запроса базы данных: первое перенесено в SELECT COUNT(1) FROM ( ... ), вторыми являются сырые данные.

Установка:

  • MySQL 5.0 с помощью PremiumSoft Navicat (LIMIT 10000 в запросе);
  • SQL Server Express использование 2005 года Microsoft SQL Server Management Studio Express;
  • Oracle XE с помощью МН Разработчика / Разработчика SQL 7 (ограниченный 10 000 строк).

Оставленное внешнее объединение:

SELECT a.ID, a.SomeText, COUNT(b.ID)
FROM Blah a
LEFT OUTER JOIN Blah b ON a.ID= b.ParentID
GROUP BY a.ID, a.SomeText
  • MySQL: 5.0: 51,469 с / 49,907 с
  • SQL Server: 0 (1) / 9 с (2)
  • Oracle XE: 1,297 с / 2,656 с

(1) Фактически мгновенный (подтверждение другого пути выполнения)
(2) Впечатляющее рассмотрение его возвращает все строки, не 10,000

Просто идет для показа значения реальной базы данных. Кроме того, удаление поля SomeText оказало значительное влияние на работу MySQL. Также не было большого различия между пределом 10 000 и не наличием его с MySQL (улучшающий производительность фактором 4-5). Oracle имела его просто, потому что МН Разработчик / Разработчик SQL блевали, когда это совершило нападки 100M использование памяти.

Связанный подзапрос:

SELECT ID, SomeText, (SELECT COUNT(1) FROM Blah WHERE ParentID= a.ID) ChildCount
FROM Blah a
  • MySQL: 8,844 с / 11,10 с
  • SQL Server: 0s / 6 с
  • Oracle: 0,046 с / 1,563 с

Таким образом, MySQL лучше фактором 4-5, Oracle приблизительно вдвое более быстра, и SQL Server возможно только немного быстрее.

Точка остается: версия связанного подзапроса быстрее во всех случаях.

Другое преимущество связанных подзапросов состоит в том, что они - синтаксически инструмент для очистки и легче расшириться. Этим я подразумеваю, что, если Вы хотите провести подсчет в наборе других таблиц, каждый может быть включен как другой избранный объект чисто и легко. Например: вообразите запись клиентов к счетам, где те счета были или не оплачены, просрочены или заплачены. С подзапросом, который легок:

SELECT id,
  (SELECT COUNT(1) FROM invoices WHERE customer_id = c.id AND status = 'UNPAID') unpaid_invoices,
  (SELECT COUNT(1) FROM invoices WHERE customer_id = c.id AND status = 'OVERDUE') overdue_invoices,
  (SELECT COUNT(1) FROM invoices WHERE customer_id = c.id AND status = 'PAID') paid_invoices
FROM customers c

Совокупная версия намного более ужасна.

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

13
ответ дан 18 December 2019 в 07:57
поделиться

Я полагаю, что это - то, что Вы пытаетесь сделать:

SELECT P.PK_ID, P.Column1, P.Column2, COUNT(C.PK_ID)
FROM
    Parent P
    LEFT JOIN Child C ON C.PK_ID = P.FK1
GROUP BY
    P.PK_ID, P.Column1, P.Column2
4
ответ дан 18 December 2019 в 07:57
поделиться

Объяснение того, почему @cletus является неправильным.

Во-первых, опоры при проведении исследования.

Во-вторых, Вы делаете его неправильно.

Объяснение:

Исходный запрос:

EXPLAIN
SELECT ID, (SELECT COUNT(1) FROM Blah WHERE ParentID= a.ID) as ChildCount
FROM Blah a

Результат:

    "Seq Scan on blah a  (cost=0.00..145180063607.45 rows=2773807 width=4)"
    "  SubPlan"
    "    ->  Aggregate  (cost=52339.61..52339.63 rows=1 width=0)"
    "          ->  Seq Scan on blah  (cost=0.00..52339.59 rows=10 width=0)"
    "                Filter: (parentid = $0)"

Что происходит, когда Вы переноситесь в "избранном количестве (1)":

EXPLAIN SELECT count(1) FROM (
SELECT ID, (SELECT COUNT(1) FROM Blah WHERE ParentID= a.ID) as ChildCount
FROM Blah a) as bar
    "Aggregate  (cost=52339.59..52339.60 rows=1 width=0)"
    "  ->  Seq Scan on blah a  (cost=0.00..45405.07 rows=2773807 width=0)"

Заметить различие?

Оптимизатор достаточно умен, чтобы видеть, что он не должен делать подзапроса. Таким образом, не случается так, что связанные подзапросы быстры; это, это, НЕ ДЕЛАЯ ИХ быстро :-).

К сожалению, это не может сделать того же для левого внешнего объединения, так как количество результатов не предопределяется первым сканированием.

Урок № 1: планы запросов говорят Вам адскую партию. Плохой дизайн эксперимента получает Вас в проблему.

Урок № 1.1: Если Вы не должны делать, соединение, любой ценой, не делает.

Я создал тестовый набор данных примерно 2,7 миллионов запросов.

Левое внешнее объединение - без обертки - выполнило 171 757 мс на моем ноутбуке.

Связанный подзапрос... Я обновлю, когда это закончится, я на уровне 700K мс, и это все еще работает.

Урок № 2: Когда кто-то говорит Вам смотреть на план запросов и заявления, которые что он показывает, алгоритмический порядок различия... смотрят на план запросов.

2
ответ дан 18 December 2019 в 07:57
поделиться

Вызов printf («% d», i); в main () не завершает вывод в новой строке, поведение программы определяется реализацией.

Я утверждаю, что на моей реализации программа, которая не может записать завершающую новую строку для последней строки, всегда печатает 5 , за которой следует новая строка в качестве своей последней строки.

Таким образом, выход будет всегда 5, независимо от определения change () .: -)

(Другими словами, какой смысл в таких вопросах, если они не предназначены для запуска на определенном оборудовании, компиляторе и т.д.)

-121--2227023-

Для преобразования HTML в PDF в C # используйте ABCpdf .

ABCpdf может использовать движки рендеринга Gecko или трезубец, поэтому HTML-таблица будет выглядеть так же, как в FireFox и Internet Explorer.

Имеется интерактивная демонстрация ABCpdf по адресу www.abcpdfeditor.com. Это можно использовать для проверки того, как таблицы будут отображаться в первую очередь, без необходимости загрузки и установки программного обеспечения.

Для рендеринга целых веб-страниц потребуются функции AddImageUrl или AddImageHtml. Но если все, что вы хотите сделать, это просто добавить HTML стилизованный текст, то вы можете попробовать функцию AddHtml, как показано ниже:

Doc theDoc = new Doc();
theDoc.FontSize = 72;
theDoc.AddHtml("<b>Some HTML styled text</b>");
theDoc.Save(Server.MapPath("docaddhtml.pdf"));
theDoc.Clear();

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

-121--190â1-

Все запросы предполагают, что порядок ввода родительских дочерних узлов является последовательным. Если дочерний узел из одного из первых узлов введен в конце и его идентификатор или PK выше, то запрос не работает.

0
ответ дан 18 December 2019 в 07:57
поделиться