Paramaterize Case Statement Использование ODBC - PullRequest
1 голос
/ 17 апреля 2020

Я выбираю данные из БД MS SQL, используя запрос, подобный следующему:

$result = odbc_prepare(
   $connection, 
   "SELECT 
      t1.id, thumbnail, description, brand, vendor, 
      case 
         when ".implode(' AND ', $exactSearchTermDesc)." then 1 
         else 0 
      end as exactdescriptionmatch, 
      case 
         when ".implode(' AND ', $exactSearchTermMarkDesc)."". $exactThesaurusMarkDesc ." 
         then 1 else 0 
      end as exactcontentmatch, 
      case 
         when ".implode(' AND ', $searchTermDesc)."" . $thesaurusDesc ." then 1 
         else 0 
      end as descriptionmatch, 
      case 
         when ".implode(' AND ', $searchTermMarkDesc)."". $thesaurusMarkDesc ." then 1 
         else 0 
      end as contentmatch 
   FROM Martin.dbo.item_search"
);
odbc_execute($result);

Как мне параметризировать переменные в операторах case?

Я пытался, например, просто пытаться параматизировать первую переменную с этим безрезультатно ...

$exactSearchTermDesc = implode(' AND ', $exactSearchTermDesc);
$result = odbc_prepare(
   $connection, 
   "SELECT 
      t1.id, thumbnail, description, brand, vendor, 
      case when ? then 1 else 0 end as exactdescriptionmatch, 
      case when ".implode(' AND ', $exactSearchTermMarkDesc)."". $exactThesaurusMarkDesc ." then 1 else 0 end as exactcontentmatch, 
      case when ".implode(' AND ', $searchTermDesc)."" . $thesaurusDesc ." then 1 else 0 end as descriptionmatch, 
      case when ".implode(' AND ', $searchTermMarkDesc)."". $thesaurusMarkDesc ." then 1 else 0 end as contentmatch 
   FROM Martin.dbo.item_search"
);
odbc_execute($result array($exactSearchTermDesc));

Переменные, как показано ниже. $ term - это пользовательский ввод.

$searchTerms = explode(' ', $terms);
            $exactSearchTermDesc = array();
            $exactSearchTermMarkDesc = array();
            $searchTermDesc = array();
            $searchTermMarkDesc = array();
            foreach ($searchTerms as $term) {
                $term = trim($term);
                if (!empty($term)) {
                    $exactSearchTermDesc[] = "description LIKE '$term %'";
                    $exactSearchTermMarkDesc[] = "contains(marketingDescription, '$term')";
                    $searchTermDesc[] = "description LIKE '%$term%'";
                    $searchTermMarkDesc[] = "marketingDescription LIKE '%$term%'";
                    $searchTermVendor[] = "vendor LIKE '%$term%'";
                }
            }

$exactThesaurusDesc = " Or " . implode(' AND ', $exactThesaurusDesc); 
                $exactThesaurusMarkDesc = " Or " . implode(' AND ', $exactThesaurusMarkDesc);
                $thesaurusDesc = " Or " . implode(' AND ', $thesaurusDesc);
                $thesaurusMarkDesc = " Or " . implode(' AND ', $thesaurusMarkDesc);
                $thesaurusVendor = " Or " . implode(' AND ', $thesaurusVendor);

1 Ответ

1 голос
/ 18 апреля 2020

Рассмотрите возможность перефакторинга вашего SQL процесса. Вместо объединения воедино динамических c SQL компонентов на прикладном уровне (т. Е. PHP), которые могут повлиять на удобочитаемость и удобство обслуживания, рассмотрим несколько самостоятельных объединений во временную таблицу из терминов со следующим определением:

CREATE TABLE tmp_terms (
  id IDENTITY(1,1) NOT NULL,
  term VARCHAR(255),
  thesauraus_indicator BIT
)

В частности, выполнить агрегированный запрос по объединенным совпадениям, где MIN возвращает 0 или 1. При таком подходе запрос такой же SQL поддерживается, но базовые условия , заполненные PHP, будут компонентом Dynami c.

Ниже приведена часть, не являющаяся тезаурусом предыдущей CASE logi c. Кроме того, поскольку CONTAINS не может использовать другой столбец, а только литеральное значение, в выражениях JOIN используется LIKE с начальным и конечным подстановочными знаками.

SELECT 
   i.id, i.thumbnail, i.description, i.brand, i.vendor, 
   MIN(case 
           when exact_desc.term IS NOT NULL
           then 1 
           else 0 
       end) AS exact_description_match, 
   MIN(case 
           when market_match.term IS NOT NULL
           then 1 
           else 0 
       end) AS market_match, 
   MIN(case 
           when desc_match.term IS NOT NULL
           then 1 
          else 0 
       end) AS desc_match, 
   MIN(case 
           when vendor_match.term IS NOT NULL
           then 1 
           else 0 
       end) AS vendor_match 

FROM Martin.dbo.item_search i
LEFT JOIN tmp_terms exact_desc
   ON i.description LIKE CONCAT(exact_desc.term, ' %')
   AND exact_desc.thesaurus_indicator = 0
LEFT JOIN tmp_terms market_match
   ON i.marketingDescription LIKE CONCAT('%', market_match.term, '%')
   AND market_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms desc_match
   ON i.description LIKE CONCAT('%', desc_match.term, '%')
   AND desc_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms vendor_match
   ON i.vendor LIKE CONCAT('%', vendor_match.term, '%')
   AND vendor_match.thesaurus_indicator = 0

GROUP BY i.id, i.thumbnail, i.description, i.brand, i.vendor

Для интеграции тезауруса сопоставьте, используйте UNION в CTE или подзапросе перед агрегированием во внешнем запросе.

WITH sub AS
   (SELECT 
       i.id, i.thumbnail, i.description, i.brand, i.vendor, 
       exact_desc.term AS exact_desc_term, market_match.term AS market_match_term, 
       desc_match.term AS desc_match_term, vendor_match.term AS vendor_match_term

    FROM Martin.dbo.item_search i
    LEFT JOIN tmp_terms exact_desc
       ON i.description LIKE CONCAT(exact_desc.term, ' %')
       AND exact_desc.thesaurus_indicator = 0
    LEFT JOIN tmp_terms market_match
       ON i.marketingDescription LIKE CONCAT('%', market_match.term, '%')
       AND market_match.thesaurus_indicator = 0
    LEFT JOIN tmp_terms desc_match
       ON i.description LIKE CONCAT('%', desc_match.term, '%')
       AND desc_match.thesaurus_indicator = 0
    LEFT JOIN tmp_terms vendor_match
       ON i.vendor LIKE CONCAT('%', vendor_match.term, '%')
       AND vendor_match.thesaurus_indicator = 0

    UNION

    SELECT 
       i.id, i.thumbnail, i.description, i.brand, i.vendor, 
       th_exact_desc.term, th_market_match.term, 
       th_desc_match.term, th_vendor_match.term

    LEFT JOIN tmp_terms th_exact_desc
       ON i.description LIKE CONCAT(th_exact_desc.term, ' %')
       AND th_exact_match.thesaurus_indicator = 1
    LEFT JOIN tmp_terms th_market_match
       ON i.marketingDescription LIKE CONCAT('%', th_market_match.term, '%')
       AND th_market_match.thesaurus_indicator = 1
    LEFT JOIN tmp_terms th_desc_match
       ON i.description LIKE CONCAT('%', th_desc_match.term, '%')
       AND th_desc_match.thesaurus_indicator = 1
    LEFT JOIN tmp_terms th_vendor_match
       ON i.vendor LIKE CONCAT('%', th_vendor_match.term, '%')
       AND th_vendor_match.thesaurus_indicator = 1
   )

SELECT sub.id, sub.thumbnail, sub.description, sub.brand, sub.vendor, 
       MIN(case 
               when sub.exact_desc_term IS NOT NULL
               then 1 
               else 0 
           end) AS exact_description_match, 
       MIN(case 
               when sub.market_match_term IS NOT NULL
               then 1 
               else 0 
           end) AS market_match, 
       MIN(case 
               when sub.desc_match_term IS NOT NULL
               then 1 
              else 0 
           end) AS desc_match, 
       MIN(case 
               when sub.vendor_match_term IS NOT NULL
               then 1 
               else 0 
           end) AS vendor_match 
FROM sub
GROUP BY sub.id, sub.thumbnail, sub.description, sub.brand, sub.vendor

Примечание. Приведенные выше запросы могут нуждаться в корректировке в соответствии с потребностями конечного использования и тестированием. Эффективность может варьироваться в зависимости от количества поисковых запросов. Но, в конечном счете, концепция заключается в том, чтобы взаимодействовать с данными в наборах, а не долго объединять CASE WHEN... logi c, которые растут с поисковыми терминами.


Наконец, в PHP просто очистите и заполните временная таблица с новыми значениями с использованием параметризации и запуска любого из указанных выше окончательных запросов:

odbc_exec($connection, "DELETE FROM tmp_terms");

$stmt = odbc_prepare($connection,
                     "INSERT INTO tmp_terms (term, thesaurus_indicator) 
                      VALUES (?, ?)");

// NON-THESAURUS TERMS
foreach($search_terms as $t) {
   odbc_execute($stmt, array($t, 0));
}

// THESAURUS TERMS
foreach($th_search_terms as $t) {
   odbc_execute($stmt, array($t, 1));
}

// RUN FINAL SELECT QUERY
$result = odbc_exec($connection, "my_final_above_query");

while(odbc_fetch_row($result)){
         for($i=1; $i<=odbc_num_fields($result); $i++){
             // ... odbc_result($result,$i);
    }
}
...