Mysql:具有最小值的行,并根据大小写对条件进行优先级排序

时间:2019-05-10 08:46:08

标签: mysql join having

我有两个表,第一个表是: docs 和另一个表: doc_val

使用doc_id作为表docs的外键

我需要获取符合特定条件的docs(包括val的{​​{1}},typecriteria的列表,例如:{ {1}}

在获取此文档列表时,我还需要确保给定的doc_val的{​​{1}}是最小的。并且还要确保doc_val.criteria = 'L' and docs.rev = 1,考虑到存在doc_val.val的其他情况,对于给定的doc_iddoc_val.type = 'D'的最小值为doc_val.type = 'D',我们应该简单地得到doc_val

doc_id

使用此查询,如果我只是将doc_val.val作为where条件的一部分,则会删除所有没有类型为D的文档。

CREATE TABLE IF NOT EXISTS `docs` (
  `id` int(6) unsigned NOT NULL,
  `rev` int(3) unsigned NOT NULL,
  `content` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `doc_val` (
  `id` int(6) unsigned NOT NULL,
  `doc_id` int(6) unsigned NOT NULL,
  `val` int(3) unsigned NOT NULL,
  `type` varchar(2) NOT NULL,
  `criteria` varchar(2) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

INSERT INTO `docs` (`id`, `rev`, `content`) VALUES
  ('1', '1', 'The earth is flat'),
  ('2', '1', 'One hundred angels can dance on the head of a pin'),
  ('3', '1', 'The earth is flat and rests on a bull\'s horn'),
  ('4', '4', 'The earth is like a ball.');

INSERT INTO `doc_val` (`id`, `doc_id`, `val`, `type`, `criteria`) VALUES
  ('1', '1', 100, 'D', 'L'),
  ('2', '1', 101, 'D', 'L'),
  ('3', '1', 80, 'H', 'L'),
  ('4', '2', 10, 'H', 'S'),
  ('5', '2', 90, 'H', 'L'),
  ('6', '3', 100, 'D', 'L'),
  ('7', '3', 100, 'D', 'L');

enter image description here

如果我们根本不将b.type = 'D'视为条件,则该条件的输出有效,但

SELECT a.id, a.rev, a.content, b.val, b.type, b.criteria
FROM `docs` a
  JOIN `doc_val` b ON b.doc_id = a.id
  WHERE a.`rev` = 1 and b.type = 'D' and b.criteria = 'L'      
GROUP BY `a`.`id`
HAVING min(b.`val`)

最终预期输出:

enter image description here

但是从技术上讲,没有type=D作为条件,我应该收到SELECT a.id, a.rev, a.content, b.val, b.type, b.criteria FROM `docs` a JOIN `doc_val` b ON b.doc_id = a.id WHERE a.`rev` = 1 and b.criteria = 'L' GROUP BY `a`.`id` HAVING min(b.`val`) 的输出,如下:

enter image description here

  1. 所以我可能在使用type=D时做错了任何方向都是有用的。

  2. 是否可以用doc.id = 1来对HAVING进行优先级排序,这样当使用doc_val.type的行具有优先权时,如果它不存在,则只需取最小值即可不用考虑doc_val.type = D

3 个答案:

答案 0 :(得分:1)

您可以在下面尝试-

DEMO

SELECT 
    *
FROM
    (SELECT 
        a.id, a.rev, a.content, MIN(b.val) val, b.type, b.criteria
    FROM
        `docs` a
    JOIN `doc_val` b ON b.doc_id = a.id
    WHERE
        a.`rev` = 1 AND b.criteria = 'L'
    GROUP BY a.id , a.rev , a.content , b.type , b.criteria) A
WHERE
    val IN (SELECT 
            MAX(val)
        FROM
            (SELECT 
                a.id, a.rev, a.content, MIN(b.val) val, b.type, b.criteria
            FROM
                `docs` a
            JOIN `doc_val` b ON b.doc_id = a.id
            WHERE
                a.`rev` = 1 AND b.criteria = 'L'
            GROUP BY a.id , a.rev , a.content , b.type , b.criteria) B
        WHERE
            A.content = B.content)

输出:

id  rev content                                            val   type  criteria                  
1   1   The earth is flat                                   100  D     L
2   1   One hundred angels can dance on the head of a pin   90   H     L
3   1   The earth is flat and rests on a bull's horn        100  D     L

答案 1 :(得分:1)

经过多次测试,我想到了这样的东西:

SELECT a.id, a.rev, a.content, c.val, c.type, c.criteria
FROM `docs` a
  JOIN 
  (SELECT doc_id,criteria,
       LEFT(gctv,1) AS 'Type',
       SUBSTRING(SUBSTRING_INDEX(REPLACE(gctv,',',' '),' ',1),2) AS 'val' 
  FROM
    (SELECT doc_id,criteria,
            CASE 
            WHEN INSTR(ctv,@type) <> 0 THEN REPLACE(MID(ctv,INSTR(ctv,@type)) ,' ','')
            WHEN INSTR(ctv,@type)=0 THEN REPLACE(ctv,' ','') END AS gctv 
     FROM          
        (SELECT doc_id ,criteria,
                GROUP_CONCAT(tv ORDER BY val ASC) AS ctv 
          FROM
             (SELECT doc_id,criteria,val,
                     CONCAT_WS(' ',TYPE,VAL) tv
                FROM doc_val
                     WHERE criteria='L'
              )A
           GROUP BY doc_id
          )B, (SELECT @type:='D') temp
      )C) c
    ON a.id=c.doc_id
    WHERE rev=1;

我尝试将其分解:

这是查询的核心

SELECT doc_id,criteria,val,
       CONCAT_WS(' ',TYPE,VAL) tv
            FROM doc_val
                WHERE criteria='L';

基本上,这里要做的是将Typeval合并为一列,条件为criteria='L'。结果如下:

result1

第一个外部查询

SELECT doc_id, criteria,
       GROUP_CONCAT(tv ORDER BY val ASC) AS ctv 
  FROM
      (SELECT doc_id,criteria,val,
              CONCAT_WS(' ',TYPE,VAL) tv
       FROM doc_val
          WHERE criteria='L'
       )A
  GROUP BY doc_id

根据核心查询结果及其按GROUP_CONCAT分组的结果执行doc_id。产生如下结果:

result2

您注意到,在GROUP_CONCAT中,我已经向ORDER BY val ASC添加了一个条件。这将从左到右先返回最小值。

然后我们转到第三个查询:

SELECT doc_id,criteria,
        CASE 
        WHEN INSTR(ctv,@type) <> 0 THEN REPLACE(MID(ctv,INSTR(ctv,@type)) ,' ','')
        WHEN INSTR(ctv,@type)=0 THEN REPLACE(ctv,' ','') END AS gctv 
 FROM          
    (SELECT doc_id ,
            GROUP_CONCAT(tv ORDER BY val ASC) AS ctv 
      FROM
         (SELECT doc_id,val,
                 CONCAT_WS(' ',TYPE,VAL) tv
            FROM doc_val
                 WHERE criteria='L'
          )A
       GROUP BY doc_id
      )B, (SELECT @type:='D') temp

在这里使用类型条件,而不是一一键入(我之前做过),而是使用一个变量,因此,如果类型条件不再是'D',则只需更改它从变量。您将看到这里使用了更多运算符。

INSTR用于查找'ctv'列中是否具有@type变量,该变量是否设置为'D'。这将返回“ D”的起始位置。例如,在第二张图像中,第一个结果是[H 80,D 100,D 101],因此运算符INSTR将查找第一个'D'事件的位置,该位置将返回6(从左到右计数)右边的空格和逗号)。第二个返回0,因为它在列内未找到任何DCASE WHEN将检查值是否为0,然后将按原样返回列中的值,如果值<> 0,则将基于从INSTR(ctv,@type)中提取的位置返回值。这就是为什么我添加了另一个运算符以从位置(MID)获取列值的原因。我加入了REPLACE来删除type和val之间的空格。为了进一步了解查询,我准备了以下操作的查询明细:

SELECT doc_id,criteria,
       INSTR(ctv,@type),
       MID(ctv,INSTR(ctv,@type)),
       REPLACE(MID(ctv,INSTR(ctv,@type)) ,' ',''),
       CASE 
           WHEN INSTR(ctv,@type) <> 0 
           THEN REPLACE(MID(ctv,INSTR(ctv,@type)) ,' ','')
           WHEN INSTR(ctv,@type)=0 
           THEN REPLACE(ctv,' ','') 
       END AS gctv,
       SUBSTRING(REPLACE(ctv,' ',''),1)
 FROM          
    (SELECT doc_id,criteria,
            GROUP_CONCAT(tv ORDER BY val ASC) AS ctv 
      FROM
         (SELECT doc_id,criteria,val,
                 CONCAT_WS(' ',TYPE,VAL) tv
            FROM doc_val
                 WHERE criteria='L'
          )A
       GROUP BY doc_id
      )B, (SELECT @type:='D') temp

上面的查询将返回以下内容:

result3

最后一部分:

SELECT doc_id,
       LEFT(gctv,1) AS 'Type',
       SUBSTRING(SUBSTRING_INDEX(REPLACE(gctv,',',' '),' ',1),2) AS 'val' 

REPLACE的第一个运算符是将逗号更改为空格(请参见上图中的gctv结果)。然后使用SUBSTRING_INDEX,它在列中使用第一个'type + val',然后SUBSTRING将从位置2返回值-该值是从val列中获取的(假设您的type列仅包含一个字符。

在这里拨弄:DB Fiddle

答案 2 :(得分:0)

这应该给您您想要的结果

在此处查看演示 http://sqlfiddle.com/#!9/0e32ec/32

Select *
from(
SELECT a.id, a.rev, a.content, b.val, b.type, b.criteria
FROM `docs` a
  JOIN `doc_val` b ON b.doc_id = a.id
  WHERE a.`rev` = 1 and b.criteria = 'L'      
Order by `a`.`id`, FIELD(b.type, 'D','H','A'), b.`val` ) as sub
GROUP BY `id`

输出:

id  rev content                                            val   type  criteria                  
1   1   The earth is flat                                   100  D     L
2   1   One hundred angels can dance on the head of a pin   90   H     L
3   1   The earth is flat and rests on a bull's horn        100  D     L