对值进行分组以便按最小值排序,然后对其他字段进行分类,然后按该值进行排序

时间:2017-11-07 09:22:24

标签: sql oracle

我想按聚合值对数据进行排序,然后按其他字段对数据进行排序,然后按非聚合值进行排序。

我有以下架构:

CREATE TABLE priority_t (
  id          NUMERIC(10),
  priority    NUMERIC(10),
  CONSTRAINT priority_t_pk PRIMARY KEY (id)
);

CREATE TABLE value_t (
  id          NUMERIC(10),
  value       NUMERIC(10),
  CONSTRAINT value_t_pk PRIMARY KEY (id)
);

CREATE TABLE file_t (
  id          NUMERIC(10),
  CONSTRAINT file_t_pk PRIMARY KEY (id)
);

CREATE TABLE main_t (
  id          NUMERIC(10),
  priority_id NUMERIC(10),
  value_id    NUMERIC(10),
  file_id     NUMERIC(10),
  CONSTRAINT main_t_pk PRIMARY KEY (id),
  CONSTRAINT priority_t_fk FOREIGN KEY (priority_id) REFERENCES priority_t(id),
  CONSTRAINT value_t_fk FOREIGN KEY (value_id) REFERENCES value_t(id),
  CONSTRAINT file_t_fk FOREIGN KEY (file_id) REFERENCES file_t(id)
);

然后我插入以下数据:

INSERT INTO priority_t (id, priority) VALUES (1, 10);
INSERT INTO priority_t (id, priority) VALUES (2, 20);

INSERT INTO value_t (id, value)       VALUES (1, 987);
INSERT INTO value_t (id, value)       VALUES (2, 876);
INSERT INTO value_t (id, value)       VALUES (3, 765);
INSERT INTO value_t (id, value)       VALUES (4, 654);

INSERT INTO file_t (id)               VALUES (111);
INSERT INTO file_t (id)               VALUES (222);
INSERT INTO file_t (id)               VALUES (333);
INSERT INTO file_t (id)               VALUES (444);

INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (1, 1, 1, 111);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (2, 2, 1, 111);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (3, 2, 2, 222);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (4, 1, 2, 333);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (5, 2, 3, 111);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (6, 1, 4, 444);
INSERT INTO main_t  (id, priority_id, value_id, file_id) VALUES (7, 2, 4, 444);

COMMIT;

我希望得到以下结果:

 min_priority | priority | value | value_id | file_id
   (hidden)   |          |       | (hidden) |        
--------------+----------+-------+----------+---------
           10 |       10 |   654 |        4 |     444 
           10 |       20 |   654 |        4 |     444
           10 |       10 |   876 |        2 |     333
           10 |       10 |   987 |        1 |     111
           10 |       20 |   987 |        1 |     111
           20 |       20 |   765 |        3 |     111
           20 |       20 |   876 |        2 |     222

我知道如何对它们进行排序:

ORDER BY min_value ASC, value ASC, value_id ASC, priority ASC

但我的问题是我不知道如何对值本身进行分组:我的行和/或值都不断重复。

我最接近的尝试如下:

WITH listing AS (
  SELECT m.id             AS main_id,
         p.id             AS priority_id,
         p.priority       AS priority,
         v.id             AS value_id,
         v.value          AS value,
         f.id             AS file_id
    FROM main_t m
           INNER JOIN priority_t p ON m.priority_id = p.id
           INNER JOIN value_t v    ON m.value_id = v.id
           INNER JOIN file_t f     ON m.file_id = f.id
)
SELECT min_p.min_priority AS min_priority,
       listing.priority   AS priority,
       listing.value      AS value,
       listing.file_id    AS file_id
  FROM listing,
       (
         SELECT min(min_p_value.min_priority) AS min_priority,
                min_p_value.value_id          AS min_value_id,
                listing.file_id               AS file_id
           FROM listing,
                (
                  SELECT min(listing.priority) AS min_priority,
                         listing.value         AS value,
                         listing.value_id      AS value_id
                    FROM listing
                   GROUP BY listing.value, listing.value_id
                ) min_p_value
          WHERE listing.value = min_p_value.value
            AND listing.value_id = min_p_value.value_id
            AND min_p_value.min_priority = min_priority
          GROUP BY min_p_value.value_id, listing.file_id
       ) min_p
 WHERE min_p.min_value_id = listing.value_id
   AND min_p.file_id = listing.file_id
 ORDER BY min_p.min_priority ASC,
          listing.value ASC,
          listing.value_id ASC,
          listing.priority;

这会返回以下错误结果:

 MIN_PRIORITY   PRIORITY      VALUE    FILE_ID
------------- ---------- ---------- ----------
           10         10        654        444
           10         20        654        444
           10         10        876        333
           10         20        876        222 <-- incorrect, should have a min_priority of 20, and therefore be the last
           10         10        987        111
           10         20        987        111
           20         20        765        111

我如何实现我的期望?

3 个答案:

答案 0 :(得分:1)

这应该有效:

select (select min(priority)
          from main_t mm
          join priority_t tt
            on tt.id = mm.priority_id
         where mm.value_id = m.value_id) as min_priority,
       p.priority as priority,
       v.value as value,
       m.value_id,
       m.file_id
  from main_t m
  join priority_t p
    on p.id = m.priority_id
  join value_t v
    on v.id = m.value_id
 order by 1, 3, 4, 2;

它通过value_id确定最低优先级。 您可以按列号按顺序订购结果。

答案 1 :(得分:0)

我已经在MySQL(而不是Oracle)中编写了这段代码并且不确定CTE语法,所以我用一个名为min_priority的临时表替换了WITH子句,但你当然可以用CTE替换临时表。当我在MySQl上运行它时,我得到了你想要的相同结果。

还不确定你是否需要左连接或内连接,这两个都适用于这个例子。

-- find min priority for each value
create temporary table if not exists min_priority as (
select m.value_id, min(p.priority) as min_pri
from main_t m
inner join priority_t p on m.priority_id = p.id
group by m.value_id
);

-- just join all the tables, including min_priority, and order the result
select mp.min_pri, p.priority, v.value, v.id, f.id
from main_t m
left join priority_t p on m.priority_id = p.id
left join value_t v on m.value_id = v.id
left join file_t f on m.file_id = f.id
left join min_priority mp on mp.value_id = v.id
order by mp.min_pri asc, v.value asc, v.id asc, p.priority asc;

作为旁注,我不会使用多级内部查询作为问题中的示例,因为它们会影响性能。

答案 2 :(得分:0)

当您需要汇总和非汇总值时,请使用analytic functions。我想,那会是这样的:

select *
  from (select min(p.priority) over (partition by v.id, v.value, f.id) min_priority, 
               p.priority, v.value, v.id value_id, f.id file_id 
          from main_t m
          join priority_t p on m.priority_id = p.id
          join value_t v    on m.value_id = v.id
          join file_t f     on m.file_id = f.id)
  order by min_priority, value, value_id, priority

结果:

MIN_PRIORITY    PRIORITY       VALUE    VALUE_ID     FILE_ID
------------ ----------- ----------- ----------- -----------
          10          10         654           4         444
          10          20         654           4         444
          10          10         876           2         333
          10          10         987           1         111
          10          20         987           1         111
          20          20         765           3         111
          20          20         876           2         222