SQL:группировать 2 таблицы как 1 с помощью соединения, объединения, а затем?
Вопрос
У меня есть 5 таблиц:
Идентификатор клиентов - название
p_orders id - id_customer - код - дата
P_ITEMS ID - ID_ORDER - Описание - цена
а h_orders и h_items — это точная копия p_orders и p_items.
Когда таблицы p_ достигают большого количества строк, я перемещаю самые старые таблицы в таблицы h_.они должны стать историей.
Итак, моя проблема: как получить данные из таблиц p_ и h_, рассматривая их как одну уникальную таблицу?
Например, я хочу получить количество заказов для каждого клиента и общую цену (всех заказов клиента), и я использую этот запрос:
SELECT
customer.id,
customer.name,
count(DISTINCT p_orders.id) AS num_orders,
sum(p_items.price) AS total_money
FROM
customer
INNER JOIN p_orders ON p_orders.id_customer = customer.id
INNER JOIN p_items ON p_items.id_order = p_orders.id
GROUP BY
customer.id,
customer.name,
p_orders.id_customer
ORDER BY
customer.id
это работает только для одного «набора» таблиц (p_ или h_).. но мне нужны оба.
Я пытался использовать UNION:
(
SELECT
customer.id,
customer.name,
count(DISTINCT p_orders.id) AS num_orders,
sum(p_items.price) AS total_money
FROM
customer
INNER JOIN p_orders ON p_orders.id_customer = customer.id
INNER JOIN p_items ON p_items.id_order = p_orders.id
GROUP BY
customer.id,
customer.name,
p_orders.id_customer
)
UNION
(
SELECT
customer.id,
customer.name,
count(DISTINCT h_orders.id) AS num_orders,
sum(h_items.price) AS total_money
FROM
customer
INNER JOIN h_orders ON h_orders.id_customer = customer.id
INNER JOIN h_items ON h_items.id_order = h_orders.id
GROUP BY
customer.id,
customer.name,
h_orders.id_customer
)
ORDER BY id ASC
Это работает, но если у клиента есть заказы как в таблицах p_, так и в таблицах h_, у меня будет 2 строки для этого клиента с двумя разными num_orders и total_money (соответственно, поступающими из таблиц p_ и h_).
Я попытался добавить идентификатор GROUP BY вне союза:
(
--SELECT 2
)
UNION
(
--SELECT 1
)
GROUP BY id
ORDER BY id ASC
но запрос не выполнен с ОШИБКА:синтаксическая ошибка в строке "GROUP" или рядом с ней в символе 948., кажется, что GROUP BY нельзя использовать таким образом.
Любое предложение?
РЕДАКТИРОВАТЬ:
Для uriDium да, все таблицы имеют столбец id в качестве первичного ключа, а соответствующие поля (также известные как p_orders.id_customer) также являются внешними ключами.Вот дамп структуры тестовой базы данных (я добавил несколько индексов и внешних ключей после создания таблицы, но не думаю, что это что-то значит):
CREATE TABLE customer (
id serial NOT NULL,
name character(50)
);
CREATE TABLE p_orders (
id serial NOT NULL,
id_customer integer NOT NULL,
date date DEFAULT now(),
code character(5)
);
CREATE TABLE p_items (
id serial NOT NULL,
id_order integer NOT NULL,
descr character(250),
price money
);
CREATE TABLE h_orders (
id integer NOT NULL,
id_customer integer NOT NULL,
date date,
code character(5)
);
CREATE TABLE h_items (
id integer NOT NULL,
id_order integer NOT NULL,
descr character(250),
price money
);
CREATE UNIQUE INDEX id_h_orders ON h_orders USING btree (id);
CREATE INDEX id_h_o_c ON h_orders USING btree (id_customer);
CREATE UNIQUE INDEX id_items_h ON h_items USING btree (id);
CREATE INDEX id_ordinr_dsve ON h_items USING btree (id_order);
ALTER TABLE ONLY customer
ADD CONSTRAINT customer_pkey (id);
ALTER TABLE ONLY p_orders
ADD CONSTRAINT p_orders_pkey PRIMARY KEY (id);
ALTER TABLE ONLY p_items
ADD CONSTRAINT p_items_pkey PRIMARY KEY (id);
ALTER TABLE ONLY stats
ADD CONSTRAINT stats_pkey PRIMARY KEY (id);
ALTER TABLE ONLY p_orders
ADD CONSTRAINT "$1" FOREIGN KEY (id_customer) REFERENCES customer(id) ON DELETE CASCADE;
ALTER TABLE ONLY p_items
ADD CONSTRAINT "$1" FOREIGN KEY (id_order) REFERENCES p_orders(id) ON DELETE CASCADE;
ALTER TABLE ONLY h_orders
ADD CONSTRAINT "$1" FOREIGN KEY (id_customer) REFERENCES customer(id) ON DELETE CASCADE;
ALTER TABLE ONLY h_items
ADD CONSTRAINT "$1" FOREIGN KEY (id_order) REFERENCES h_orders(id) ON DELETE CASCADE;
Решение
Вероятно, вам следует создать представления для двух таблиц:
CREATE VIEW All_Orders
AS
SELECT
id,
id_customer,
code,
date,
'H' AS order_type
FROM
h_orders
UNION ALL
SELECT
id,
id_customer,
code,
date,
'P' AS order_type
FROM
p_orders
CREATE VIEW All_Order_Items -- A table name of "items" is pretty bad in my opinion
AS
SELECT
id,
id_order,
description,
price,
'H' AS order_item_type
FROM
h_items
UNION ALL
SELECT
id,
id_order,
description,
price,
'P' AS order_item_type
FROM
p_items
Теперь вы можете просто присоединиться к этим взглядам.Я включил типы (P и H), чтобы вы знали, к чему теперь относится столбец «id».Если идентификаторы в ваших двух таблицах («h» и «p» могут иметь дубликаты, вам придется присоединиться к таблице Orders прямо в представлении All_Order_Items.В противном случае у вас возникнут большие проблемы с объединением двух представлений.Надеемся, что ваши столбцы идентификаторов спроектированы разумно, а не просто столбцы с автоматическим приращением или идентификаторами.
Другие советы
Вы можете попробовать это:
SELECT tbl.ID,
tbl.Name,
sum(tbl.num_orders) num_orders,
sum(tbl.total_money) total_money
FROM (
SELECT customer.id,
customer.name,
count(DISTINCT p_orders.id) AS num_orders,
sum(p_items.price) AS total_money
FROM customer
INNER JOIN p_orders
ON p_orders.id_customer = customer.id
INNER JOIN p_items
ON p_items.id_order = p_orders.id
GROUP BY customer.id, customer.name, p_orders.id_customer
UNION
SELECT customer.id,
customer.name,
count(DISTINCT h_orders.id) AS num_orders,
sum(h_items.price) AS total_money
FROM customer
INNER JOIN h_orders
ON h_orders.id_customer = customer.id
INNER JOIN h_items
ON h_items.id_order = h_orders.id
GROUP BY customer.id, customer.name, h_orders.id_customer
) tbl
GROUB BY tbl.id, tbl.name
ORDER BY tbl.id ASC
Создайте представление с объединением двух запросов, но без агрегатных функций.USe Union All, поскольку одна и та же запись отсутствует в обеих таблицах, и вам не нужно, чтобы сервер тратил время на ее поиск. Вероятно, у вас будут другие случаи, когда вам понадобится доступ к обеим таблицам в запросе.
Затем напишите свой запрос, используя представление.
код просмотра будет выглядеть примерно так (вам могут понадобиться и другие поля для других целей:
Create view customerOrders
AS
SELECT customer.id as CustomerID, customer.name, p_orders.id as OrderID, p_items.price as price
FROM customer
INNER JOIN p_orders ON p_orders.id_customer = customer.id
INNER JOIN p_items ON p_items.id_order = p_orders.id
union all
SELECT customer.id, customer.name, h_orders.id as id, H_items.price
FROM customer
INNER JOIN h_orders ON h_orders.id_customer = customer.id
INNER JOIN h_items ON h_items.id_order = h_orders.id
тогда вызов вашего запроса будет выглядеть примерно так (ничего из этого не тестировалось, возможно, потребуется корректировка)
SELECT CustomerID, customer.name, count(DISTINCT OrderID) AS num_orders,
sum(price) AS total_money
FROM customerOrders
GROUP BY CustomerID, customer.name
ORDER BY CustomerID
Насколько я знаю, SQL Server должен автоматически удалять дубликаты.Использование UNION ALL будет включать дубликаты.Я предполагаю, что SQL Server будет использовать первичные ключи как средство определения того, что является дубликатом.Состоят ли первичные ключи в этих таблицах с одним и тем же типом данных, и является ли идентификатор 1 в вашей таблице p также идентификатором 1 в вашей таблице h?
Самый простой способ сделать то, что вы ищете, — создать представления (скажем, «a_orders» и «a_items»).Представления будут просто определены следующим образом:
SELECT * FROM p_orders
UNION
SELECT * FROM h_orders
Если вы удаляете строки из a_orders при вставке их в h_orders (так что заданный порядок не будет в обеих таблицах), было бы намного эффективнее использовать UNION ALL вместо UNION.
Спасибо за все ответы, ребята..
И «способ представлений», и «способ подзапроса» Джимми Р.Хаутс работает отлично, возможно, просмотрами просто удобнее пользоваться..и они оба должны занять одно и то же время (или нет?)
Поэтому я отмечу как лучший ответ о взглядах.
В любом случае, если можно, могу ли я спросить вас, хороши ли структура и индексы, которые я использовал, или можно ли их оптимизировать?