5

我已经看过这两个问题:

但是,它们都使用聚合函数 MAX 来获得最高值或填充值,这对我的情况不起作用。

出于这个问题的目的,我已经简化了我的情况。这是我目前的数据:

数据库示例

我想获取每条路线的运营商名称,但与行进方向有关(即排序或“首选”值)。这是我的伪代码:

if(`direction` = 'west' AND `operatorName` != '') then select `operatorName`
else if(`direction` = 'north' AND `operatorName` != '') then select `operatorName`
else if(`direction` = 'south' AND `operatorName` != '') then select `operatorName`
else if(`direction` = 'east' AND `operatorName` != '') then select `operatorName`

我当前的 SQL 查询是:

SELECT route, operatorName
FROM test
GROUP BY route

这给了我分组,但我的目的是错误的运算符:

route | operatorName
--------------------
  95  | James
  96  | Mark
  97  | Justin

我试过应用一个ORDER BY子句,但GROUP BY优先。我想要的结果是:

route | operatorName
--------------------
  95  | Richard
  96  | Andrew
  97  | Justin

我不能在MAX()这里做,因为“北”按字母顺序排在“南”之前。GROUP BY在应用该条款之前,我如何明确说明我的偏好/排序?

还要记住,空字符串不是首选。

请注意,这是一个简化的示例。实际查询选择了更多字段并与其他三个表连接,但查询中没有聚合函数。

4

4 回答 4

3

您可以使用那个 MAX 示例,您只需要“伪造它”。见这里:http ://sqlfiddle.com/#!2/58688/5

SELECT *
FROM test
JOIN (SELECT 'west' AS direction, 4 AS weight
      UNION
      SELECT 'north',3
      UNION
      SELECT 'south',2
      UNION
      SELECT 'east',1) AS priority
  ON priority.direction = test.direction
JOIN (
      SELECT route, MAX(weight) AS weight
      FROM test
      JOIN (SELECT 'west' AS direction, 4 AS weight
            UNION
            SELECT 'north',3
            UNION
            SELECT 'south',2
            UNION
            SELECT 'east',1) AS priority
        ON priority.direction = test.direction
      GROUP BY route
) AS t1
  ON t1.route = test.route
    AND t1.weight = priority.weight
于 2013-11-13T20:58:40.517 回答
1

我想出了这个解决方案,但是,它很丑陋。无论如何,您可以尝试一下:

CREATE TABLE test (
  route INT,
  direction VARCHAR(20),
  operatorName VARCHAR(20)
);

INSERT INTO test VALUES(95, 'east', 'James');
INSERT INTO test VALUES(95, 'west', 'Richard');
INSERT INTO test VALUES(95, 'north', 'Dave');
INSERT INTO test VALUES(95, 'south', 'Devon');
INSERT INTO test VALUES(96, 'east', 'Mark');
INSERT INTO test VALUES(96, 'west', 'Andrew');
INSERT INTO test VALUES(96, 'south', 'Alex');
INSERT INTO test VALUES(96, 'north', 'Ryan');
INSERT INTO test VALUES(97, 'north', 'Justin');
INSERT INTO test VALUES(97, 'south', 'Tyler');

SELECT
    route,
    (SELECT operatorName
       FROM test
     WHERE route = t2.route
       AND direction =
              CASE
                WHEN direction_priority = 1 THEN 'west'
                WHEN direction_priority = 2 THEN 'north'
                WHEN direction_priority = 3 THEN 'south'
                WHEN direction_priority = 4 THEN 'east'
              END) AS operator_name
  FROM (
    SELECT
        route,
        MIN(direction_priority) AS direction_priority
      FROM (
        SELECT
            route,
            operatorName,
            CASE
              WHEN direction = 'west' THEN 1
              WHEN direction = 'north' THEN 2
              WHEN direction = 'south' THEN 3
              WHEN direction = 'east' THEN 4
            END AS direction_priority
          FROM test
      ) t
    GROUP BY route
  ) t2
;

首先,我们选择所有已direction更改为数字的记录,以便按要求的顺序排列。然后,我们GROUP通过每条路线,得到最小的方向。剩下的保留在最外面的查询中 - 根据找到的最低方向选择操作员名称。

输出:

ROUTE OPERATOR_NAME
95 理查德
96 安德鲁
97 贾斯汀

请下次不要以图片的形式附加示例数据,而是以纯文本或插入的形式(最好是SQLFiddle)。

在SQLFiddle检查此解决方案

于 2013-11-13T20:50:09.180 回答
0
select *
from routes r1
where exists (
    select 1
    from routes r2
    where r1.route_id = r2.route_id
    group by r2.route_id
    having min(case r1.direction
                 when 'west' then 1
                 when 'north' then 2
                 when 'south' then 3
                 when 'east' then 4 end) = min(case r2.direction
                                                  when 'west' then 1
                                                  when 'north' then 2
                                                  when 'south' then 3
                                                  when 'east' then 4 end)
)

演示

于 2013-11-13T20:59:35.547 回答
0

您可以使用 case 构造枚举方向,以使它们按您的顺序排序。然后对按路线划分的方向进行排名,然后只选择第一个候选。

set @c = 1;
set @r = '';

select route
     , direction
     , operatorName
  from (
         select route
              , direction
              , operatorName
              , @c := if (@r = route, @c + 1, 1) as cand
           from (
                  select route
                       , case when direction = 'west'
                              then 1
                              when direction = 'north'
                              then 2
                              when direction = 'south'
                              then 3
                              when direction = 'east'
                              then 4
                              else 5
                         end as enum_direction
                       , direction
                       , operatorName
                )
          order by
                route
              , enum_direction
       )
于 2013-11-13T20:41:36.663 回答