Взяв две таблицы, 1-ко-многим, как я могу отфильтровать таблицу многократно, а затем объединить ВСЕ совпадения в таблице 1? - PullRequest
0 голосов
/ 01 апреля 2011

Я заранее извиняюсь за длину, решение вполне может быть тривиальным, просто хотел быть максимально информативным.

Таблицы

У меня естьдве таблицы примечаний: предметы и продукты, которые являются отношением 1 ко многим.Один предмет может иметь несколько продуктов, которые являются вариациями цвета и материала.Бренд - это таблица внешних категорий, которая не должна играть большую роль в этом утверждении выбора.

Таким образом, предметом является, например, конкретная обувь, например, обувь "Park Avenue".Продуктом является, например, мерло полированная телячья кожа.И брендом будет просто Аллен Эдмондс.В целом вы получаете обувь Allen Edmonds Park Avenue из полированной телячьей кожи.

Пропущенные результаты поиска "показать почти все"

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

Текущий оператор выбора

Вот текущий выбор, который фильтруетвсе, что не имеет ручной настройки по умолчанию:

SELECT DISTINCT items.ItemId
     , items.Name
     , items.BrandCategoryId
     , items.CatalogPage
     , items.GenderId
     , items.PriceRetail
     , items.PriceSell
     , items.PriceHold
     , items.Descr
     , items.FlagStatus as ItemFlagStatus
     , products.ImagetnURL
     , products.FlagDefault
     ,  products.ProductId
     , products.Code as ProductCode
     , products.Name as ProductName
     , brands.Name as BrandName 
FROM items
   , products
   , brands 
WHERE items.ItemId = products.ItemId
  AND items.BrandCode = brands.Code
  AND items.FlagStatus != 'U'
  AND products.FlagStatus != 'U' 
  AND products.FlagDefault = 'Y';

Не мой выбор кода, я подозреваю, что часть «DISTINCT» этого утверждения - плохая идея, но я не совсем уверенкак избавиться от этого.

Большая проблема, с которой я сейчас сталкиваюсь, это заключительная строка

AND products.FlagDefault = 'Y'

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

Редактировать: Вот объяснение запроса:

+----+-------------+----------+--------+-----------------------------------------------------------+---------+---------+-------------------------+-------+--------------------------------+
| id | select_type | table    | type   | possible_keys                                             | key     | key_len | ref                     | rows  | Extra                          |
+----+-------------+----------+--------+-----------------------------------------------------------+---------+---------+-------------------------+-------+--------------------------------+
|  1 | SIMPLE      | brands   | ALL    | NULL                                                      | NULL    | NULL    | NULL                    |    38 | Using temporary                |
|  1 | SIMPLE      | products | ALL    | FlagStatus,FlagStatus_2,FlagStatus_3,flagstatusanddefault | NULL    | NULL    | NULL                    | 16329 | Using where; Using join buffer |
|  1 | SIMPLE      | items    | eq_ref | PRIMARY,BrandCode,FlagStatus,FlagStatus_2,FlagStatus_3    | PRIMARY | 4       | sherman.products.ItemId |     1 | Using where                    |
+----+-------------+----------+--------+-----------------------------------------------------------+---------+---------+-------------------------+-------+--------------------------------+
3 rows in set (0.01 sec)

А вот описание продуктов, товаров и брендов:

mysql> describe products;
+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field       | Type         | Null | Key | Default           | Extra                       |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| ProductId   | int(11)      | NO   | PRI | NULL              | auto_increment              |
| ItemId      | int(11)      | YES  |     | NULL              |                             |
| Code        | varchar(15)  | YES  | MUL | NULL              |                             |
| Name        | varchar(100) | YES  |     | NULL              |                             |
| MaterialId  | int(11)      | YES  | MUL | NULL              |                             |
| PriceRetail | decimal(6,2) | YES  |     | NULL              |                             |
| PriceSell   | decimal(6,2) | YES  |     | NULL              |                             |
| PriceHold   | decimal(6,2) | YES  |     | NULL              |                             |
| Cost        | decimal(6,2) | YES  |     | NULL              |                             |
| FlagDefault | char(1)      | NO   |     | N                 |                             |
| FlagStatus  | char(1)      | YES  | MUL | NULL              |                             |
| ImagetnURL  | varchar(50)  | YES  |     | NULL              |                             |
| ImagefsURL  | varchar(50)  | YES  |     | NULL              |                             |
| ImagelsURL  | varchar(50)  | YES  |     | NULL              |                             |
| DateStatus  | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| DateCreated | timestamp    | YES  |     | NULL              |                             |
+-------------+--------------+------+-----+-------------------+-----------------------------+
16 rows in set (0.02 sec)

mysql> describe items
    -> ;
+-----------------+--------------+------+-----+-------------------+-----------------------------+
| Field           | Type         | Null | Key | Default           | Extra                       |
+-----------------+--------------+------+-----+-------------------+-----------------------------+
| ItemId          | int(11)      | NO   | PRI | NULL              | auto_increment              |
| Code            | varchar(25)  | YES  |     | NULL              |                             |
| Name            | varchar(100) | YES  | MUL | NULL              |                             |
| BrandCode       | char(2)      | YES  | MUL | NULL              |                             |
| CatalogPage     | int(3)       | YES  |     | NULL              |                             |
| BrandCategoryId | int(11)      | YES  |     | NULL              |                             |
| TypeId          | int(11)      | YES  | MUL | NULL              |                             |
| StyleId         | int(11)      | YES  | MUL | NULL              |                             |
| GenderId        | int(11)      | YES  | MUL | NULL              |                             |
| PriceRetail     | decimal(6,2) | YES  |     | NULL              |                             |
| PriceSell       | decimal(6,2) | YES  |     | NULL              |                             |
| PriceHold       | decimal(6,2) | YES  |     | NULL              |                             |
| Cost            | decimal(6,2) | YES  |     | NULL              |                             |
| PriceNote       | longtext     | YES  |     | NULL              |                             |
| FlagTaxable     | char(1)      | YES  |     | NULL              |                             |
| FlagStatus      | char(1)      | YES  | MUL | NULL              |                             |
| FlagFeatured    | char(1)      | YES  |     | NULL              |                             |
| MaintFlagStatus | char(1)      | YES  |     | NULL              |                             |
| Descr           | longtext     | YES  |     | NULL              |                             |
| DescrNote       | longtext     | YES  |     | NULL              |                             |
| ImagetnURL      | varchar(50)  | YES  |     | NULL              |                             |
| ImagefsURL      | varchar(50)  | YES  |     | NULL              |                             |
| ImagelsURL      | varchar(50)  | YES  |     | NULL              |                             |
| DateCreated     | date         | NO   |     | 0000-00-00        |                             |
| DateStatus      | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-----------------+--------------+------+-----+-------------------+-----------------------------+
25 rows in set (0.00 sec)

mysql> describe brands;
+--------------+------------------+------+-----+-------------------+-----------------------------+
| Field        | Type             | Null | Key | Default           | Extra                       |
+--------------+------------------+------+-----+-------------------+-----------------------------+
| BrandId      | int(11) unsigned | NO   | PRI | NULL              | auto_increment              |
| Code         | varchar(6)       | YES  |     | NULL              |                             |
| PriceCode    | varchar(4)       | YES  |     | NULL              |                             |
| Name         | varchar(50)      | YES  |     | NULL              |                             |
| WebsiteURL   | varchar(50)      | YES  |     | NULL              |                             |
| LogoURL      | varchar(50)      | YES  |     | NULL              |                             |
| LogoTopURL   | varchar(50)      | YES  |     | NULL              |                             |
| BrandURL     | varchar(50)      | YES  |     | NULL              |                             |
| Descr        | longtext         | YES  |     | NULL              |                             |
| DescrShort   | longtext         | YES  |     | NULL              |                             |
| BeltDescr    | longtext         | YES  |     | NULL              |                             |
| ImageURL     | varchar(50)      | YES  |     | NULL              |                             |
| SaleImageURL | varchar(50)      | YES  |     | NULL              |                             |
| SaleCode     | varchar(6)       | YES  |     | NULL              |                             |
| SaleDateBeg  | date             | YES  |     | NULL              |                             |
| SaleDateEnd  | date             | YES  |     | NULL              |                             |
| FlagStatus   | char(1)          | YES  |     | NULL              |                             |
| DateStatus   | timestamp        | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| DateCreated  | timestamp        | YES  |     | NULL              |                             |
+--------------+------------------+------+-----+-------------------+-----------------------------+
19 rows in set (0.00 sec)

Возможности, которые я изучаю

Подвыбор, который все останавливает

У меня есть оператор выбора, который может, видеальный мир с нулевым временем выполнения, выбирая продукты для первого продуктакаждый элемент, упорядоченный этим полем flagdefault, например:

  AND products.productid =
    (select productid
     from products
     where products.itemid = items.itemid
       AND products.FlagStatus != 'U'
     order by FlagDefault='Y'
            , itemid
     limit 1);

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

Этот оператор останавливается и фактически заставляет другое использование сайта переводить операторы mysql в тупик (я полагаю, потому что чтение этих таблиц делает их недоступными в других местах).

Объединение, которое гарантирует, что одна таблица отличается, а не следующая?

Один из способов обойти это, который может сработать, это сделать:

select distinct ItemId from products ORDER BY default

А потом простоИдем дальше, чтобы получить данные для этих предметов, в частности, но я не уверен, как это сделать в одном выражении, не уверен, как правильно объединить select, и я ожидаю, что даже если сделать это выделение «отличным», в первую очередьне идеален, так как он выбирает больше, чем необходимо для начала, а затем сокращает их, но у меня нет лучшей альтернативы дляНа самом деле, окончание различий.

Советы?

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

Ответы [ 4 ]

2 голосов
/ 11 апреля 2011

Удалить из WHERE:

AND products.FlagStatus != 'U'
AND products.FlagDefault = 'Y'

Добавить к FROM:

(
   (SELECT ProductId
    FROM products
    WHERE FlagStatus != 'U' AND FlagDefault = 'Y')
UNION
   (SELECT MIN(ProductId)
    FROM products
    WHERE FlagStatus != 'U'
    GROUP BY ItemId
    HAVING MAX(FlagDefault) != 'Y')
) AS defaults

Добавить к WHERE:

AND defaults.ProductId = products.ProductId

Я использую термин «не скрытый» для строк, которые имеют FlagStatus != 'U', так как я предполагаю, что для этого предназначен флаг.

Первый SELECT дает ProductId всех продуктов по умолчанию, а второй дает ProductId для всех товаров без продукта по умолчанию. Скрытые элементы фильтруются по обоим, поэтому, если продукт по умолчанию был скрыт, вместо него отображается продукт не по умолчанию. При объединении вы получаете ProductId за каждый предмет, у которого есть не скрытый продукт.

Я предполагаю, что FlagDefault может иметь только значения 'Y' или 'N'. Второй запрос отфильтровывает элементы, имеющие продукт по умолчанию, используя MAX(FlagDefault), который работает, потому что 'Y' > 'N'.

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

Я проверил этот запрос, но я не проверял его с вашим исходным запросом, поскольку у меня нет никаких значимых данных (читай: ваших данных), чтобы проверить его. Это работает, поэтому комбинация также должна работать. По той же причине у меня нет реальных цифр о производительности - и я также не эксперт по производительности запросов (больше похоже на новичка). Однако, как я слышал, подзапросы в предложении WHERE должны быть плохими для производительности, но в предложении FROM они должны быть в порядке. Итак, проверьте это, я надеюсь, что это достаточно быстро и подходит для работы.

Как уже упоминалось, если у вас нет индекса для столбцов products.ItemId и BrandCode, вы обязательно должны их добавить. Вы должны также рассмотреть вопрос о том, будет ли требоваться, чтобы для каждого элемента было выбрано одно выбранное вручную значение по умолчанию, или, может быть, выбрано выбранное вручную значение по умолчанию и всегда используются случайные. Еще одна вещь, которую стоит рассмотреть, если вам действительно нужны данные о продукте, когда нет значений по умолчанию - можете ли вы жить без URL-адреса изображения, названия продукта (использовать имя элемента?) И кода продукта для этих продуктов?

Редактировать: Еще одна возможность: вы можете изменить products.FlagDefault на items.DefaultProductId. Таким образом было бы легче выяснить, есть ли у товара продукт по умолчанию, и для него применяется только один продукт по умолчанию.

1 голос
/ 05 апреля 2011

Я не уверен, что полностью понимаю FlagStatus и FlagDefault.Для элемента без значения по умолчанию у всех его продуктов есть products.FlagDefault != 'Y'?

Если да, можете ли вы попробовать это?Он (будем надеяться, вернет все элементы с NULL в полях продуктов для элементов без значения по умолчанию):

SELECT items.ItemId
     , items.Name
     , items.BrandCategoryId
     , items.CatalogPage
     , items.GenderId
     , items.PriceRetail
     , items.PriceSell
     , items.PriceHold
     , items.Descr
     , items.FlagStatus as ItemFlagStatus
     , products.ImagetnURL
     , products.FlagDefault
     , products.ProductId
     , products.Code as ProductCode
     , products.Name as ProductName
     , brands.Name as BrandName 
FROM items
  LEFT JOIN
     products 
    ON items.ItemId = products.ItemId
    AND products.FlagDefault = 'Y'
  JOIN
     brands 
    ON items.BrandCode = brands.Code
;

Значение LEFT JOIN:

  LEFT JOIN
     products 
    ON items.ItemId = products.ItemId
    AND products.FlagDefault = 'Y'

эквивалентно:

  LEFT JOIN
    ( SELECT *
      FROM products
       WHERE products.FlagDefault = 'Y'
    ) AS p
    ON items.ItemId = p.ItemId

Итак, как вы и просите, " фильтрует результаты для наиболее конкретной таблицы и только-затем присоединяется к восходящему потоку к ... "

При использовании LEFT JOIN s результат может быть другим, если вы поместите условия фильтрации, которые у вас есть, в предложении ON или позже после всех JOINS в предложении WHERE.

1 голос
/ 08 апреля 2011

Я не уверен в производительности, так как вы не указали структуру и размеры таблиц или не объяснили план, но как насчет UNION между вашим первым запросом (товары с продуктами по умолчанию) и запросом, который выбирает только один товар на единицу, толькодля товаров без продукта по умолчанию?

Это немного долго, но попробуйте - дайте мне знать, если он даст вам правильные данные и сколько времени это займет ...

(SELECT items.ItemId
     , items.Name
     , items.BrandCategoryId
     , items.CatalogPage
     , items.GenderId
     , items.PriceRetail
     , items.PriceSell
     , items.PriceHold
     , items.Descr
     , items.FlagStatus as ItemFlagStatus
     , products.ImagetnURL
     , products.FlagDefault
     , products.ProductId
     , products.Code as ProductCode
     , products.Name as ProductName
     , brands.Name as BrandName
FROM items
     JOIN products ON items.ItemId = products.ItemId
     JOIN brands ON items.BrandCode = brands.Code
WHERE items.FlagStatus != 'U'
  AND products.FlagStatus != 'U'
  AND products.FlagDefault = 'Y'
GROUP BY items.ItemId)
UNION
(SELECT items.ItemId
     , items.Name
     , items.BrandCategoryId
     , items.CatalogPage
     , items.GenderId
     , items.PriceRetail
     , items.PriceSell
     , items.PriceHold
     , items.Descr
     , items.FlagStatus as ItemFlagStatus
     , products.ImagetnURL
     , products.FlagDefault
     , products.ProductId
     , products.Code as ProductCode
     , products.Name as ProductName
     , brands.Name as BrandName
FROM items
     JOIN products ON items.ItemId = products.ItemId
     JOIN brands ON items.BrandCode = brands.Code
WHERE items.FlagStatus != 'U'
  AND products.FlagStatus != 'U'
  AND products.FlagDefault != 'Y'
  AND items.ItemId NOT IN 
      (SELECT DISTINCT itemId 
       FROM products 
       WHERE products.FlagDefault = 'Y')
GROUP BY items.ItemId)
1 голос
/ 01 апреля 2011
SELECT
        items.ItemId,
        items.Name,
        items.BrandCategoryId,
        items.CatalogPage,
        items.GenderId,
        items.PriceRetail,
        items.PriceSell,
        items.PriceHold,
        items.Descr,
        items.FlagStatus as ItemFlagStatus,
        T3.ImagetnURL,
        T3.FlagDefault,
        T3.ProductId,
        T3.Code as ProductCode,
        T3.Name as ProductName,
        brands.Name as BrandName 
FROM    items INNER JOIN
        (
            SELECT DISTINCT
                T1.ItemId,
                T1.ImagetnURL,
                T1.FlagDefault
                T1.ProductId,
                T1.Code
                T1.Name,
                T1.FlagStatus
            FROM
                products AS T1 LEFT JOIN
                products AS T2 ON T1.products.ProductId = T2.products.ProductId
                    AND T2.FlagDefault = 'Y'
        ) AS T3 ON items.ItemId = T3.ItemId INNER JOIN 
        brands ON items.BrandCode = brands.Code
WHERE   items.FlagStatus != 'U'
        AND T3.FlagStatus != 'U'
...