Какой самый быстрый способ получить максимальное значение столбца в таблице? - PullRequest
1 голос
/ 14 июня 2011

INFORMIX-SQL 4.10.DC1 (SE Engine), в DOS 6.22, под Microsoft Virtual PC 2007, в Windows 7!

EDIT: Looking for Jonathan Leffler's wisdom on this one!

У меня есть дочерняя таблица с именем транзакция и родительская таблица с именем customer.

К этим таблицам присоединяется customer.pk_id SERIAL = transaction.fk_id INTEGER.

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

В таблице транзакций также есть столбец с именемaction_number SERIAL суникальный индекс.

Когда для любого клиента добавляется новая транзакция, в EOF добавляется новая строка с наибольшим номером транзакции. Примечание: Informix не поддерживает физическую кластеризацию в файле данных после создания индекса.

Поэтому, когда я хочу напечатать квитанцию ​​клиента о последней введенной транзакции, я делаю следующий запрос:

SELECT * 
  FROM customer c, 
       transaction t
 WHERE c.pk_id = t.fk_id
   AND t.trx_num = (SELECT MAX(trx_num)
                      FROM transaction)

РЕДАКТИРОВАТЬ: существуют следующие ИНДЕКСЫ:

UNIQUE INDEX ON customer(pk_id) {SERIAL}
UNIQUE CLUSTER INDEX ON customer(last_name,sur_name,first_name,middle_name) {CHAR(30's)}
UNIQUE INDEX ON customer(ident_type,ident_num,ident_state,ident_country) {CHARS(n)}

UNIQUE CLUSTER INDEX ON transaction(fk_id) {INT}
UNIQUE INDEX ON transaction(trx_num) {SERIAL}

EDIT: EXPLAIN результаты для вышеуказанного запроса:

QUERY:
------
SELECT *
  FROM customer c,
       transaction t,
 WHERE c.pk_id   = t.fk_id           
   AND t.trx_num = (SELECT MAX(trx_num) 
                      FROM transaction)

Estimated Cost: 14
Estimated # of Rows Returned: 2

1) f.transaction: INDEX PATH

    (1) Index Keys: trx_num 
        Lower Index Filter: f.transaction.trx_num = <subquery> 

2) f.customer: INDEX PATH

    (1) Index Keys: pk_id 
        Lower Index Filter: f.customer.pk_id = f.transaction.fk_id 

    Subquery:
    ---------
    Estimated Cost: 3
    Estimated # of Rows Returned: 1

    1) f.trx_num: INDEX PATH

        (1) Index Keys: trx_num 

Похоже, что оптимизатор запросов выполняет полное сканирование таблицы транзакции, чтобы найти самый большой номер транзакции, чья новая строка всегда помещается в EOF, но поскольку транзакции сгруппированы по fk_id, остальная часть транзакциичисла разбросаны по всей таблице.

Есть ли лучший способ получить более быстрое время ответа на запрос?

Поможет ли CREATE UNIQUE INDEX trxnumidx ON transaction(transaction_number) DESCENDING быстрый поиск МАКСИМАЛЬНОГО номера транзакции?

РЕДАКТИРОВАТЬ: у меня есть аналогичный запрос, который быстро извлекает требуемую транзакцию и информацию о клиенте, так что пользователь может распечатать последнюю введенную транзакцию или любую ранее введенную транзакцию, но он требует от пользователя ввода номера транзакции:

SELECT * 
      FROM customer c, 
           transaction t
     WHERE c.pk_id = t.fk_id
       AND t.trx_num = $trxnum  {ace input variable, type INT}

Что сбивает с толку меня в этом запросе, когда пользователь вручную вводит номер транзакции, так это то, что получение происходит мгновенно (общая стоимость = 2), где, как при автоматическом с MAX (общая стоимость = 14).. Причина, по которой я решил сделать этот запрос автоматическим, заключается в том, что в прошлом, когда пользователи вручную вводили номер транзакции, иногда они непреднамеренно вводили неправильный номер транзакции, который оказался действительным, и, не осознавая этого, они подписывали и даваликлиенту квитанцию ​​с неверной информацией!

РЕДАКТИРОВАТЬ: DBINFO('sqlca.sqlerrd1') будет более эффективным способом поиска последней вставленной строки?

Ответы [ 2 ]

1 голос
/ 15 июня 2011

Поскольку вам нужен максимальный номер транзакции для fk_id, я думаю, что я ожидал бы создать (необязательно УНИКАЛЬНЫЙ) индекс:

CREATE {UNIQUE} INDEX ix_lookup1 ON Transaction(FK_ID, Transaction_Number);

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

Затем я бы ожидал использовать запрос, подобный тому, который предлагал oers :

SELECT * 
  FROM customer AS c, transaction AS t
 WHERE c.pk_id = t.fk_id
   AND c.pk_id = 12345678         -- crucial to limit the data to one return row
   AND t.transaction_number =
          (SELECT MAX(transaction_number)
             FROM transaction AS t2
            WHERE t2.fk_id = c.pk_id)

Это все еще коррелированный подзапрос, но, учитывая, что используется номер клиента, будет возвращаться только одна строка данных, и оптимизатор откровенно признает, что это так.В зависимости от языка программирования, который я использовал, я мог бы хорошо организовать использование подзапроса:

SELECT * 
  FROM customer AS c, transaction AS t
 WHERE c.pk_id = t.fk_id
   AND c.pk_id = 1234567
   AND t.transaction_number =
          (SELECT MAX(transaction_number)
             FROM transaction AS t2
            WHERE t2.fk_id = 1234567)

Или даже:

SELECT * 
  FROM customer AS c, transaction AS t
 WHERE c.pk_id = t.fk_id
   AND c.pk_id = 1234567
   AND t.transaction_number =
          (SELECT MAX(transaction_number)
             FROM transaction AS t2
            WHERE t2.fk_id = 1234567)

Вы можете использовать заполнители или создать SQLиз подходящей строки.

Еще лучше, я бы избежал этой проблемы, захватив вставленное серийное значение сразу после вставки, чтобы (а) если программа когда-либо стала многопользовательской, она все равно работала и(б) можно было бы избежать всей проблемы MAX, и запрос мог бы стать следующим:

SELECT * 
  FROM customer AS c, transaction AS t
 WHERE c.pk_id = t.fk_id
   AND c.pk_id = 1234567
   AND t.transaction_number = 23456789;

Обратите внимание, что с c.pk_id = 1234567 для клиента, указанным в качестве параметра запроса, вам может даже не потребоватьсяИндекс я предложил.И где бы я ни использовал число, было бы неплохо использовать заполнитель для значения, которое будет предоставлено при выполнении запроса.

Помните, что вы используете программное обеспечение, которое достаточно старое, чтобы пить, не говоря уже оголос;оптимизатор не так хорош, как в более современной СУБД.

0 голосов
/ 14 июня 2011

Можете ли вы попробовать использовать JOIN вместо (неявного СОЕДИНЕНИЯ с) WHERE:

SELECT * 
FROM customer
  JOIN transaction
    ON customer.pk_id = transaction.fk_id
WHERE transaction.transaction_number = ( SELECT MAX(transaction_number)
                                         FROM transaction)

или этого:

SELECT * 
FROM customer
  JOIN transaction
    ON customer.pk_id = transaction.fk_id
  JOIN ( SELECT MAX(transaction_number) AS max_tn
         FROM transaction
       ) AS tr
    ON transaction.transaction_number = tr.max_tn

Без использования JOIN,Я могу подумать об этом:

SELECT * 
  FROM customer
     , transaction
 WHERE customer.pk_id = transaction.fk_id
   AND transaction.transaction_number IN 
       ( SELECT MAX(transaction_number)
         FROM transaction
       )

Возможно, IN (SELECT ...) лучше оптимизирован, чем = (SELECT ...)


Вы также можете попробовать расположить таблицы (и условия) в обратном порядке.:

SELECT * 
  FROM transaction, customer 
 WHERE transaction.transaction_number
       = ( SELECT MAX(transaction_number)
           FROM transaction )
  AND customer.pk_id = transaction.fk_id

Не знаю, сработает ли следующее или возникнет ошибка, но стоит попробовать:

SELECT * 
FROM 
      ( SELECT MAX(transaction_number) AS max_tn
        FROM transaction
      )
  AS tr
   , transaction
   , customer
WHERE transaction.transaction_number = tr.max_tn
  AND customer.pk_id = transaction.fk_id
...