2016-02-26 3 views
1

У меня есть запрос, который преобразует больше строки в одну строку. Я хотел знать, есть ли какая-либо техника лучше, чем. Чтобы проиллюстрировать наш случай, я применил простую связь с пользователями и смоделированные запросы, похожие на наше приложение.PostgreSQL - Объединение данных из нескольких строк в одну строку

table: Users  PrimaryKey: UserId 
--------------------------------------- 
| UserId | UserDetails1 | UserDetails2| 
--------------------------------------- 
| 1  | name1  | Addr1  | 
| 2  | name2  | Addr2  | 
--------------------------------------- 

table: UserCars Unique Constraint(UserId, CarType) 
        index on userid, cartype 
------------------------------------------- 
| UserId | CarType | RedCount | BlueCount | 
------------------------------------------- 
| 1 | SUV | 1  | 0  | 
| 1 | sedan | 1  | 2  | 
| 2 | sedan | 1  | 0  | 
-------------------------------------------  
Consider CarType as an enum type with values SUV and sedan only 

Применение должно принести UserDetails1, сумма (RedCount), сумма (BlueCount), RedCount внедорожник, BlueCount внедорожник, седан RedCount, седан BlueCount для каждого пользователя в одном запросе.

Для приведенного выше примера, результат должен быть как

-------------------------------------------------------------------------------- 
| UserId | UserDetails1 | TotalRed |TotalBlue|SUVRed|SUVBlue|sedanRed|sedanBlue| 
-------------------------------------------------------------------------------- 
| 1  | name1  | 2  | 2 | 1 | 2 | 1 | 0 | 
| 2  | name2  | 1  | 0 | 0 | 0 | 1 | 0 | 
-------------------------------------------------------------------------------- 

В настоящее время наш запрос, как показано ниже

SELECT 
--User Information 
u.UserId, u.UserDetails1, 
--Total Counts by color 
count_by_colour.TotalRed, count_by_colour.TotalBlue, 
    -- Counts by type 
COALESCE(suv.red, 0) AS SUVRed, COALESCE(suv.blue, 0) AS SUVBlue, 
COALESCE(sedan.red, 0) AS sedanRed, COALESCE(sedan.blue, 0) AS sedanBlue 
FROM Users u 
JOIN (
    SELECT c.UserId, SUM(RedCount) as TotalRed, 
    SUM(BlueCount) AS TotalBlue 
    FROM UserCars c GROUP BY UserId 
) count_by_colour 
ON (u.UserId = count_by_colour.UserId) 
LEFT JOIN (
    SELECT UserId, RedCount AS red, BlueCount AS blue 
    FROM UserCars 
    WHERE CarType = 'SUV') suv 
ON (u.UserId = suv.UserId) 
LEFT JOIN (
    SELECT UserId, RedCount AS red, BlueCount AS blue 
    FROM UserCars 
    WHERE CarType = 'sedan') sedan 
ON (u.UserId = sedan.UserId) 

Хотя запрос извлекает данные, как и ожидалось, я хотел бы знать, если есть любой метод лучше этого. В этом примере я дал только два типа (внедорожник и седан), но в нашем оригинальном приложении, которое связано с маркетингом, есть больше типов, что означает больше левых объединений.

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

Спасибо,
Ravi

+0

Хотя этот запрос немного некрасиво, логически правильно, и я не могу придумать способ, чтобы упростить его. –

+0

Вы уверены, что хотите как 'mysql', так и' postgresql'? –

+0

@TimBiegeleisen Я немного изменил его и прокомментировал ниже.Он работает лучше, чем исходный запрос. –

ответ

2

Как @Giorgos Betsos указал c чтобы избежать левых объединений в моем первоначальном запросе. Спасибо Гиоргосу Бетсосу за это. Но причина отказа от ответа @Giorgos Betsos в качестве ответа на оригинальный вопрос заключается в том, что группировка в таблице Users, использующая все столбцы из таблицы пользователей, занимает больше времени. В реальном случае из таблицы пользователей будет выбрано большее количество столбцов, и, следовательно, этого следует избегать.

Я слегка изменил свой запрос следующим образом

SELECT 
--User Information 
u.UserId, u.UserDetails1, 
--Total Counts by color 
temp.TotalRed, temp.TotalBlue, 
    -- Counts by type 
temp.SUVRed, temp.SUVBlue, 
temp.sedanRed, temp.sedanBlue 
FROM Users u 
JOIN (SELECT userid, 
     SUM(RedCount) AS TotalRed, SUM(BlueCount) AS TotalBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN RedCount END), 0) AS SUVRed, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN BlueCount END), 0) AS SUVBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN RedCount END), 0) AS SedanRed, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN BlueCount END), 0) AS SedanBlue 
FROM usercars GROUP BY userid) temp 
ON (temp.userid = u.userid) 

Я побежал оба эти запросы к того же набора данных и план запроса выглядит следующим образом

Для запроса в ответ Йоргос Betsos в


GroupAggregate (cost=34407.59..41848.99 rows=99999 width=25) (actual time=477.323..644.976 rows=99999 loops=1) 
    -> Sort (cost=34407.59..34903.09 rows=198197 width=25) (actual time=477.303..513.956 rows=199974 loops=1) 
     Sort Key: u.userid, u.userdetails1 
     Sort Method: external merge Disk: 7608kB 
     -> Hash Right Join (cost=3375.98..12227.15 rows=198197 width=25) (actual time=83.339..265.419 rows=199974 loops=1) 
       Hash Cond: (uc.userid = u.userid) 
       -> Seq Scan on usercars uc (cost=0.00..3176.51 rows=199951 width=16) (actual time=0.009..48.687 rows=199951 loops=1) 
       -> Hash (cost=1636.99..1636.99 rows=99999 width=13) (actual time=83.137..83.137 rows=99999 loops=1) 
        Buckets: 4096 Batches: 8 Memory Usage: 570kB 
        -> Seq Scan on users u (cost=0.00..1636.99 rows=99999 width=13) (actual time=0.009..34.343 rows=99999 loops=1) 
Total runtime: 649.600 ms 

Для измененного запроса, приведенного в этом комментарии

Hash Join (cost=3376.40..23359.86 rows=100884 width=61) (actual time=87.938..392.103 rows=99976 loops=1) 
    Hash Cond: (temp.userid = u.userid) 
    -> Subquery Scan on temp (cost=0.42..15883.52 rows=100884 width=52) (actual time=0.064..231.107 rows=99976 loops=1) 
     -> GroupAggregate (cost=0.42..14874.68 rows=100884 width=16) (actual time=0.063..216.605 rows=99976 loops=1) 
       -> Index Scan using user_cartype on usercars (cost=0.42..8367.18 rows=199951 width=16) (actual time=0.036..44.917 rows=199951 loops=1) 
    -> Hash (cost=1636.99..1636.99 rows=99999 width=13) (actual time=87.635..87.635 rows=99999 loops=1) 
     Buckets: 4096 Batches: 8 Memory Usage: 570kB 
     -> Seq Scan on users u (cost=0.00..1636.99 rows=99999 width=13) (actual time=0.008..36.204 rows=99999 loops=1) 
Total runtime: 395.397 ms 

Еще раз спасибо Giorgos Betsos за его предложение.

Спасибо,
Ravi

+0

Вы можете также использовать функцию 'crosstab' из расширения tablefunc, если вы делаете опорную точку. – hruske

2

Вы можете использовать условную агрегацию:

SELECT u.UserId, u.UserDetails1, 
     SUM(RedCount) AS TotalRed, SUM(BlueCount) AS TotalBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN RedCount END), 0) AS SUVRed, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN BlueCount END), 0) AS SUVBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN RedCount END), 0) AS SedanRed, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN BlueCount END), 0) AS SedanBlue 
FROM Users AS u  
LEFT JOIN UserCars AS uc 
    ON u.UserId = uc.UserId 
GROUP BY u.UserId, u.UserDetails1 

Demo here

+0

Большое спасибо за предложение. Я слегка изменил запрос и использовал его. Я прокомментировал измененный запрос ниже. –

1
SELECT 
--User Information 
u.UserId, u.UserDetails1, 
--Total Counts by color 
temp.TotalRed, temp.TotalBlue, 
    -- Counts by type 
temp.SUVRed, temp.SUVBlue, 
temp.sedanRed, temp.sedanBlue 
FROM Users u 
(SELECT userid, 
     SUM(RedCount) AS TotalRed, SUM(BlueCount) AS TotalBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN RedCount END), 0) AS SUVRed, 
     COALESCE(SUM(CASE WHEN CarType = 'SUV' THEN BlueCount END), 0) AS SUVBlue, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN RedCount END), 0) AS SedanRed, 
     COALESCE(SUM(CASE WHEN CarType = 'sedan' THEN BlueCount END), 0) AS SedanBlue 
FROM usercars GROUP BY userid) temp 
ON (temp.userid = u.userid) 
+0

Пожалуйста, представьте/объясните свой ответ словами. Не просто отправляйте код, так как мы хотим понять, как ваш ответ решает проблему, насколько бы она ни была хороша. –

+0

этот код в точности соответствует моему комментарию –

Смежные вопросы