Mysql: строки, имеющие минимальное значение и приоритет, где условие основано на регистре - PullRequest
5 голосов
/ 10 мая 2019

У меня есть две таблицы, первая таблица: docs и другой стол: doc_val

с doc_id в качестве внешнего ключа из таблицы docs

Мне нужно получить список docs (включая val, type и criteria из doc_val), который соответствует определенным условиям, например, например: doc_val.criteria = 'L' and docs.rev = 1

При получении этого списка документов мне также необходимо убедиться, что doc_val.val для данного doc_id является минимальным. И также убедитесь, что doc_val.type = 'D', учитывая, что существует doc_val.type = 'D' ELSE, мы просто должны получить doc_val для данного doc_id, который имеет минимум doc_val.val.

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');

В этом запросе, если я возьму b.type = 'D' просто как часть условия where, я потеряю все документы, которые не имеют тип 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

Но технически без type=D как условие, я должен был бы получить вывод для doc.id = 1 как:

enter image description here

  1. Так что я, вероятно, делаю что-то не так с использованием HAVING любое направление будет полезным.

  2. Можно ли расставить приоритеты doc_val.type с doc_val.type = D, чтобы, когда строка с type = D имела приоритет, если ее не существовало, просто взять с минимальным значением без учета type?

Ответы [ 3 ]

1 голос
/ 10 мая 2019

Вы можете попробовать ниже -

ДЕМО

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 голос
/ 11 мая 2019

После нескольких тестов у меня получилось что-то вроде этого:

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';

По сути, здесь происходит объединение Type и val в один столбец с условием 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, потому что он не нашел D внутри столбца.CASE 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 состоит только изодного символа).

Скрипка здесь: Скрипка БД

0 голосов
/ 10 мая 2019

Это должно дать вам желаемый результат

Смотри демо здесь 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
...