Считать как дубликаты аналогичные значения - PullRequest
0 голосов
/ 30 января 2019

У меня есть следующие таблицы:

    Orders 
    order_id
    9
    10
    11

    Order_details 
    order_id, product_id  
    9,        7    
    10,       5
    10,       6
    11,       6
    11,       7

    Products 
    product_id, product_name, price
    5,          potato,       4.99
    6,          potato *,     7.5
    7,          orange,       7.99

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

Как добавить к этому запросу возможность подсчитывать ТОЛЬКО заказы, если есть один товар без дополнительных символов и другой с ним?

, например, "картошка" и«potato» будет игнорироваться, «potato *» и «potato *» также будут игнорироваться, но порядок с «potato» и «potato *» будет в результатах

select od.order_id
from order_details od join
     products p
     on od.product_id = p.product_id
group by od.order_id
having count(p.product_name) > count(distinct p.product_name)

Ответы [ 2 ]

0 голосов
/ 30 января 2019

Вы можете цепочка в (самой длинной) начальной подстроке:


CREATE TABLE products (
        product_id INTEGER NOT NULL PRIMARY KEY
        , product_name text
        , price DECIMAL(8,2)
        );

INSERT  INTO products(product_id, product_name, price) VALUES
    (5,          'potato',       4.99)
    ,(6,          'potato *',     7.5)
    ,(1,          'potatoes',     7.48) -- added these
    ,(2,          'potatoe',     7.49)  --
    ,(7,          'orange',       7.99)
        ;

ALTER TABLE products
        ADD COLUMN parent_id INTEGER REFERENCES products(product_id)
        , ADD COLUMN canonical_id INTEGER REFERENCES products(product_id);

UPDATE products
SET canonical_id = product_id;

SELECT*FROM products;

WITH xxx AS  ( select product_id, product_name
        , length(product_name) AS len
        FROM products
        )
UPDATE products dst
SET parent_id = src.product_id
FROM xxx src
-- WHERE position (src.product_name IN dst.product_name) = 1
WHERE dst.product_name LIKE src.product_name ||'%'::text
AND src.len > 4
AND src.len < length(dst.product_name)
 AND NOT EXISTS (
        SELECT * FROM xxx nx
        WHERE dst.product_name LIKE nx.product_name|| '%'::text
        AND nx.len < length(dst.product_name)
        AND nx.len > src.len
        AND nx.product_id <> dst.product_id
        )
        ;

SELECT*FROM products;

WITH yyy AS  ( select product_id, product_name
        , length(product_name) AS len
        FROM products
        )
UPDATE products dst
SET canonical_id = src.product_id
FROM yyy src
WHERE dst.product_name LIKE src.product_name ||'%'::text
AND src.len > 4
AND src.len < length(dst.product_name)
 AND NOT EXISTS (
        SELECT * FROM yyy nx
        WHERE dst.product_name LIKE nx.product_name|| '%'::text
        AND nx.len < src.len
        )
        ;

SELECT*FROM products;

Результат:


DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 5
ALTER TABLE
UPDATE 5
 product_id | product_name | price | parent_id | canonical_id 
------------+--------------+-------+-----------+--------------
          5 | potato       |  4.99 |           |            5
          6 | potato *     |  7.50 |           |            6
          1 | potatoes     |  7.48 |           |            1
          2 | potatoe      |  7.49 |           |            2
          7 | orange       |  7.99 |           |            7
(5 rows)

UPDATE 3
 product_id | product_name | price | parent_id | canonical_id 
------------+--------------+-------+-----------+--------------
          5 | potato       |  4.99 |           |            5
          7 | orange       |  7.99 |           |            7
          6 | potato *     |  7.50 |         5 |            6
          2 | potatoe      |  7.49 |         5 |            2
          1 | potatoes     |  7.48 |         2 |            1
(5 rows)

UPDATE 3
 product_id | product_name | price | parent_id | canonical_id 
------------+--------------+-------+-----------+--------------
          5 | potato       |  4.99 |           |            5
          7 | orange       |  7.99 |           |            7
          6 | potato *     |  7.50 |         5 |            5
          2 | potatoe      |  7.49 |         5 |            5
          1 | potatoes     |  7.48 |         2 |            5
(5 rows)

Примечание: для этого может потребоваться дополнительная эвристическая настройка.(или даже ручное редактирование)

0 голосов
/ 30 января 2019

Одним из вариантов может быть просто сделать простую замену, чтобы удалить * из названия продукта:

SELECT
    od.order_id
FROM order_details od
INNER JOIN products p
    ON od.product_id = p.product_id
GROUP BY
    od.order_id
HAVING
    COUNT(DISTINCT p.product_name) <>
    COUNT(DISTINCT REPLACE(p.product_name, ' *', ''));

Демо

Демонстрация приведена для MySQL, но тот же запрос должен выполняться по крайней мере для нескольких других баз данных.

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

Редактировать:

Так как вы используете Postgres, мы можем фактически сделать большецелевая замена регулярных выражений:

SELECT
    od.order_id
FROM order_details od
INNER JOIN products p
    ON od.product_id = p.product_id
GROUP BY
    od.order_id
HAVING
    COUNT(DISTINCT p.product_name) <>
    COUNT(DISTINCT REGEXP_REPLACE(p.product_name, ' \*$', ''));

Демо

...