Я хочу сделать запрос в MySQL или postgres, который будет сгенерирован из 4 таблиц.

См. Следующие таблицы.

Мне нужен запрос postgres или sql для таблицы матриц, которая определена ниже.

Как я могу добиться этого с помощью SQL?

Заранее спасибо.

таблица: цели

+----+-------------+
| id | name        |
+----+-------------+
|  1 | 9999999991  |
|  2 | 9999999992  |
|  3 | 9999999993  |
|  4 | 9999999994  |
|  5 | 9999999995  |
|  6 | 9999999996  |
|  7 | 9999999997  |
|  8 | 9999999998  |
+----+-------------+

таблица: целевые_группы

+----+-------------+
| id | name        |
+----+-------------+
|  1 | Group 1     |
|  2 | Group 2     |
|  3 | Group 3     |
|  4 | Group 4     |
+----+-------------+

таблица: Target_groups_map

+----+-----------+--------------+
| id |targets    | target_groups|
+----+-----------+--------------+
|  1 | 9999999991|    1         |
|  2 | 9999999992|    1         |
|  3 | 9999999993|    2         |
|  4 | 9999999994|    2         |
|  5 | 9999999995|    3         |
|  6 | 9999999996|    3         |
|  6 | 9999999997|    4         |
|  6 | 9999999998|    4         |
+----+-----------+--------------+

таблица: Call_details

+----+-----------+--------------+
| id | caller    | called       |
+----+-----------+--------------+
|  1 | 9999999995| 9999999996   |
|  2 | 9999999992| 9999999998   |
|  3 | 9999999993| 9999999998   |
|  4 | 9999999994| 9999999991   |
|  5 | 9999999995| 9999999998   |
|  6 | 9999999996| 9999999992   |
|  6 | 9999999991| 9999999993   |
|  6 | 9999999992| 9999999998   |
+----+-----------+--------------+

Таблица матриц , которую я хочу

+--------+--------+--------+--------+--------+
|        | Group 1| Group 2| Group 3| Group 4|
+--------+--------+--------+--------+--------+
| Group 1|     -  |     1  |     -  |     2  |
| Group 2|     1  |     -  |     -  |     1  |
| Group 3|     1  |     -  |     1  |     1  | 
| Group 4|     -  |     -  |     -  |     -  |
+--------+--------+--------+--------+--------+
1
dhruv jadia 7 Сен 2016 в 07:50

3 ответа

Лучший ответ

В Postgres вам понадобится расширение tablefunc для создания сводная таблица:

create extension if not exists tablefunc;

Запрос с crosstab():

select * from crosstab($$
    select t1.name caller_name, t2.name called_name, count
    from target_groups t1
    cross join target_groups t2
    left join (
        select c1, c2, count(*)::int
        from (
            select g1.target_groups c1, g2.target_groups c2
            from call_details c
            join target_groups_map g1 on c.caller = g1.targets
            join target_groups_map g2 on c.called = g2.targets
            ) c
        group by 1, 2
        order by 1, 2
        ) c
    on t1.id = c1 and t2.id = c2
$$) 
as ct (" " text, "Group 1" int, "Group 2" int, "Group 3" int, "Group 4" int)

         | Group 1 | Group 2 | Group 3 | Group 4 
---------+---------+---------+---------+---------
 Group 1 |         |       1 |         |       2
 Group 2 |       1 |         |         |       1
 Group 3 |       1 |         |       1 |       1
 Group 4 |         |         |         |        
(4 rows)

Тот же запрос с агрегатной функцией string_agg() вместо этого из crosstab():

select caller_name as " ", string_agg(coalesce(count::text, '-'), ', ') matrix
from (
    select t1.name caller_name, t2.name called_name, count
    from target_groups t1
    cross join target_groups t2
    left join (
        select c1, c2, count(*)::int
        from (
            select g1.target_groups c1, g2.target_groups c2
            from call_details c
            join target_groups_map g1 on c.caller = g1.targets
            join target_groups_map g2 on c.called = g2.targets
            ) c
        group by 1, 2
        order by 1, 2
        ) c
    on t1.id = c1 and t2.id = c2
    ) sub
group by 1
order by 1;

         |   matrix   
---------+------------
 Group 1 | -, 1, -, 2
 Group 2 | 1, -, -, 1
 Group 3 | 1, -, 1, 1
 Group 4 | -, -, -, -
(4 rows)
3
klin 10 Сен 2016 в 11:30

Этот оператор sql:

SELECT 
  tg1.name as caller, 
  tg2.name as called, 
  SUM(cd.caller IS NOT NULL AND cd.called IS NOT NULL) as cnt 
FROM 
  Target_groups tg1 JOIN Target_groups tg2   
  LEFT JOIN 
    Target_groups_map tgm1 ON tg1.id=tgm1.target_groups
  LEFT JOIN 
    Target_groups_map tgm2 ON tg2.id=tgm2.target_groups
  LEFT JOIN
    Call_details cd ON tgm1.targets=cd.caller AND tgm2.targets=cd.called
GROUP BY
  tg1.id,tg2.id;

Дает:

+---------+---------+------+
| caller  | called  | cnt  |
+---------+---------+------+
| Group 1 | Group 1 |    0 |
| Group 1 | Group 2 |    1 |
| Group 1 | Group 3 |    0 |
| Group 1 | Group 4 |    2 |
| Group 2 | Group 1 |    1 |
| Group 2 | Group 2 |    0 |
| Group 2 | Group 3 |    0 |
| Group 2 | Group 4 |    1 |
| Group 3 | Group 1 |    1 |
| Group 3 | Group 2 |    0 |
| Group 3 | Group 3 |    1 |
| Group 3 | Group 4 |    1 |
| Group 4 | Group 1 |    0 |
| Group 4 | Group 2 |    0 |
| Group 4 | Group 3 |    0 |
| Group 4 | Group 4 |    0 |
+---------+---------+------+

Но если вы хотите отформатировать ТОЧНО, как вы его распечатали, вам понадобится подготовленный оператор sql, а затем сделайте то, что говорит г-н Рик Джеймс, если он вам нужен динамически (заранее не знаю имена групп), вот отправная точка:

SET @sql = NULL; SELECT GROUP_CONCAT(DISTINCT(CONCAT('"" as ',quote(name)))) INTO @sql FROM Target_groups; SET @sql = CONCAT("SELECT Target_groups.name, ", @sql, " FROM Target_groups"); PREPARE stmt FROM @sql; EXECUTE stmt;

Statement prepared

+---------+---------+---------+---------+---------+
| name    | Group 1 | Group 2 | Group 3 | Group 4 |
+---------+---------+---------+---------+---------+
| Group 1 |         |         |         |         |
| Group 2 |         |         |         |         |
| Group 3 |         |         |         |         |
| Group 4 |         |         |         |         |
+---------+---------+---------+---------+---------+

Ps - это поможет любому, кто пытается решить этот + тест:

create table Targets (id int, name bigint) engine=innodb;
insert into Targets values (1,9999999991),(2,9999999992),(3,9999999993),(4,9999999994),(5,9999999995),(6,9999999996),(7,9999999997),(8,9999999998);

create table Target_groups (id int, name varchar(16)) engine=innodb;
insert into Target_groups values (1,'Group 1'),(2,'Group 2'),(3,'Group 3'),(4,'Group 4');

create table Target_groups_map (id int,targets bigint,target_groups int) engine=innodb;
insert into Target_groups_map values (1,9999999991,1),(2,9999999992,1),(3,9999999993,2),(4,9999999994,2),(5,9999999995,3),(6,9999999996,3),(6,9999999997,4),(6,9999999998,4);

create table Call_details (id int,caller bigint,called bigint) engine=innodb;
insert into Call_details values (1,9999999995,9999999996),(2,9999999992,9999999998),(3,9999999993,9999999998),(4,9999999994,9999999991),(5,9999999995,9999999998),(6,9999999996,9999999992),(6,9999999991,9999999993),(6,9999999992,9999999998);
0
gfunk 10 Сен 2016 в 05:15

MySQL ...

Это вызов. Я пройду через 2 необходимых шага:

Во-первых, давайте создадим (и отладим) запрос, в котором перечислены все пары, вызываемые вызывающим абонентом, причем некоторые из них дублируются. (Мы посчитаем их позже.)

SELECT ger.name AS er_name,
       ged.name AS ed_name
FROM Call_details AS cd
JOIN Target_groups_map AS mer  ON mer.targets = cd.caller
JOIN Target_groups_map AS med  ON med.targets = cd.called
JOIN Target_groups AS ger ON ger.id = mer.target_groups
JOIN Target_groups AS ged ON ged.id = med.target_groups;

Во-вторых, давайте сделаем поворот, посчитаем дубли, по умолчанию '-' и т. Д .:

SELECT
er_name                               AS '',
IFNULL(SUM(ed_name = 'Group 1'), '-') AS 'Group 1',
IFNULL(SUM(ed_name = 'Group 2'), '-') AS 'Group 2',
IFNULL(SUM(ed_name = 'Group 3'), '-') AS 'Group 3',
IFNULL(SUM(ed_name = 'Group 4'), '-') AS 'Group 4'
FROM ( ... ) AS y
GROUP BY er_name;

Для '...' поместите весь запрос с первого шага (не включайте ; ').

SUM может показаться странным, но вот как это работает: логическое выражение внутри него превращается в 0 (ложь) или 1 (истина), тогда SUM эффективно считается.

Если количество групп не равно 4, вам следует отказаться от попыток сделать это в SQL. Конечно, это можно было бы сделать с помощью действительно запутанной хранимой процедуры для построения второго запроса, но это причиняет мне боль.

Всякий раз, когда я делаю поворот, я пишу код на клиентском языке, например PHP.

0
Rick James 10 Сен 2016 в 04:04