После нескольких тестов у меня получилось что-то вроде этого:
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'
.Результаты выглядят так:
Первый внешний запрос
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
.Который дает следующий результат:
Вы заметили, что в 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
Запрос выше вернет следующее:
Последняя часть здесь:
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
состоит только изодного символа).
Скрипка здесь: Скрипка БД