我有两个表,第一个表是:
docs
和另一个表:
doc_val
使用doc_id
作为表docs
的外键
我需要获取符合特定条件的docs
(包括val
的{{1}},type
和criteria
的列表,例如:{ {1}}
在获取此文档列表时,我还需要确保给定的doc_val
的{{1}}是最小的。并且还要确保doc_val.criteria = 'L' and docs.rev = 1
,考虑到存在doc_val.val
的其他情况,对于给定的doc_id
,doc_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');
如果我们根本不将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`)
最终预期输出:
但是从技术上讲,没有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`)
的输出,如下:
所以我可能在使用type=D
时做错了任何方向都是有用的。
是否可以用doc.id = 1
来对HAVING
进行优先级排序,这样当使用doc_val.type
的行具有优先权时,如果它不存在,则只需取最小值即可不用考虑doc_val.type = D
?
答案 0 :(得分:1)
您可以在下面尝试-
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';
基本上,这里要做的是将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
列仅包含一个字符。
在这里拨弄: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