Самый быстрый и эффективный способ разбивки на страницы с поиском SQL DB2 - PullRequest
3 голосов
/ 09 августа 2011

Прямо сейчас я делаю два отдельных оператора SQL, один из которых выполняет SELECT COUNT(*) в основном по тем же критериям, что и оператор поиска. Я не лучший в этих заявлениях и иногда немного медленный, и я хотел бы знать, есть ли лучший способ делать то, что я делаю. Возможно, делать только один оператор SQL и еще немного работы в PHP? Вот пример "поиск содержит", у меня есть заявления для.

Во втором операторе вы увидите X между Y, которое частично рассчитывается по результату первого оператора подсчета строк.

Количество строк SQL:

SELECT COUNT(*) 
FROM itemmast 
LEFT OUTER JOIN itemweb 
ON iline = line 
AND iitem = item 
JOIN linemst 
ON iline = lline 
LEFT OUTER JOIN custord 
ON opline = iline 
AND opitem = iitem 
AND opcust = '12345' 
LEFT OUTER JOIN ordwdtl 
ON owline = iline 
AND owitem = iitem 
AND owusr ='user' 
AND owcust ='12345' 
WHERE ico = 01 
AND iecomm = 'Y'  
AND (UPPER(ITEMDESC) || UPPER(PRODDESC)) LIKE '%FOO%' 
     OR LINE LIKE '%FOO%' 
     OR UPPER(MFGNAME) LIKE '%FOO%' 
     OR UPPER(ITEM) LIKE '%FOO%' 
     OR UPPER(PRODNAME) LIKE '%FOO%' 
     OR UPPER(IDESC1 || IDESC2) LIKE '%FOO%' 
     OR UPPER(IMFGNO) LIKE '%FOO%' 
     OR UPPER(IITEM) LIKE '%FOO%') 

SQL-поиск:

SELECT * 
FROM (SELECT iline AS line, iitem AS item, rownumber() OVER (ORDER BY item) AS ROW_NUM 
      FROM itemmast 
      LEFT OUTER JOIN itemweb 
      ON iline = line 
      AND iitem = item 
      JOIN linemst 
      ON iline = lline 
      LEFT OUTER JOIN custord 
      ON opline = iline 
      AND opitem = iitem 
      AND opcust = '12345' 
      LEFT OUTER JOIN ordwdtl 
      ON owline = iline 
      AND owitem = iitem 
      AND owusr = 'user' 
      AND owcust = '12345' 
      WHERE ico = 01 
      AND iecomm = 'Y' 
      AND (UPPER(ITEMDESC) || UPPER(PRODDESC)) LIKE '%FOO%' 
           OR LINE LIKE '%FOO%' 
           OR UPPER(MFGNAME) LIKE '%FOO%' 
           OR UPPER(ITEM) LIKE '%FOO%' 
           OR UPPER(PRODNAME) LIKE '%FOO%' 
           OR UPPER(IDESC1 || IDESC2) LIKE '%FOO%' 
           OR UPPER(IMFGNO) LIKE '%FOO%' 
           OR UPPER(IITEM) LIKE '%FOO%')) 
      AS TEMP 
WHERE ROW_NUM BETWEEN 0 AND 25

1 Ответ

6 голосов
/ 09 августа 2011

Если вы пытаетесь отобразить общее количество результатов наряду с разбитыми на страницы значениями (например, «от 0 до 25 из 38»), лучшим вариантом может быть отдельное утверждение.Я пробовал несколько вещей, чтобы получить счетчики рядом с отдельными строками, но производительность (даже при умеренной тестовой базе данных) ужасна.

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

SELECT COUNT(*)
FROM view

Ранжированные строки:

SELECT *
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY item) as RANK
      FROM view) as TEMP
WHERE RANK BETWEEN 0 AND 25

Вам, конечно, нужнодобавьте относительные условия условия, но это тот тип вещей, которые должны обрабатываться представлениями.

Если вам на самом деле не нужно знать общее количество строк заранее, вы можете просто установить конечный ранг какстартовый ранг плюс некоторое смещение.Затем, когда вы отображаете свои результаты с помощью PHP, просто отредактируйте конечное значение отображения.

Некоторые случайные заметки: 1) Есть причина, по которой line не upper() d?
2)Производительность этого запроса почти не зависит от того, что вы делаете, просто из-за всех манипуляций со строками / сравнений.Можно ли устранить или игнорировать некоторые условия?Если к индикаторам, используемым в различных строковых столбцах, не применен upper (некоторые более поздние версии DB2 разрешают применять определенные скалярные функции к ключу индекса), большинство индикаторов будут полностью бесполезными (это не поможетчто вы ищете %ANYTHING% в конце концов).

Хорошо, есть «хитрый» способ сделать что-то подобное, и, похоже, у него неплохая производительность ... Попробуйте что-то вроде этого (представление, определенное первым, действительно поможет):

SELECT TEMP.*, CASE WHEN RANK = 0 THEN (SELECT COUNT(*)
                                        FROM view)
                    ELSE 0 END
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY item) as RANK
      FROM view) as TEMP
WHERE RANK BETWEEN 0 AND 25

Конечно, вы все равно должны будете иметь свое предложение where, определенное также в подвыборке ...

...