给出SQL Server 2005中的下表:
ID Col1 Col2 Col3
-- ---- ---- ----
1 3 34 76
2 32 976 24
3 7 235 3
4 245 1 792
编写产生以下结果的查询的最佳方法是什么(即产生最终列的查询 - 包含Col1,Col2和Col 3的minium值的列,每行 )?
ID Col1 Col2 Col3 TheMin
-- ---- ---- ---- ------
1 3 34 76 3
2 32 976 24 24
3 7 235 3 3
4 245 1 792 1
更新:
为了澄清(正如我在评论中所说),在真实场景中,数据库正确标准化。这些“数组”列不在实际表中,而是在报表中所需的结果集中。新要求是报告还需要此MinValue列。我无法更改基础结果集,因此我期待T-SQL获得一个方便的“走出监狱卡”。
我尝试了下面提到的CASE方法并且它有效,尽管它有点麻烦。它也比答案中说明的更复杂,因为你需要满足同一行中有两个最小值的事实。
无论如何,我认为我会发布我目前的解决方案,鉴于我的限制,它的效果非常好。它使用UNPIVOT运算符:
with cte (ID, Col1, Col2, Col3)
as
(
select ID, Col1, Col2, Col3
from TestTable
)
select cte.ID, Col1, Col2, Col3, TheMin from cte
join
(
select
ID, min(Amount) as TheMin
from
cte
UNPIVOT (Amount for AmountCol in (Col1, Col2, Col3)) as unpvt
group by ID
) as minValues
on cte.ID = minValues.ID
我会提前说我不希望这提供最好的性能,但考虑到情况(我无法重新设计所有查询只是为了新的MinValue列要求),它是一个非常优雅的“获取出了监狱卡“。
答案 0 :(得分:45)
可能有很多方法可以实现这一目标。我的建议是使用Case / When来做。有3列,它不是太糟糕。
Select Id,
Case When Col1 < Col2 And Col1 < Col3 Then Col1
When Col2 < Col1 And Col2 < Col3 Then Col2
Else Col3
End As TheMin
From YourTableNameHere
答案 1 :(得分:41)
使用CROSS APPLY
:
SELECT ID, Col1, Col2, Col3, MinValue
FROM YourTable
CROSS APPLY (SELECT MIN(d) MinValue FROM (VALUES (Col1), (Col2), (Col3)) AS a(d)) A
答案 2 :(得分:9)
SELECT ID, Col1, Col2, Col3,
(SELECT MIN(Col) FROM (VALUES (Col1), (Col2), (Col3)) AS X(Col)) AS TheMin
FROM Table
答案 3 :(得分:7)
这样做的最好方法可能是而不是这样做 - 很奇怪人们坚持以一种需要SQL“体操”来提取有意义信息的方式存储他们的数据如果你只是更好地构建你的模式,那么更简单的方法来实现所需的结果: - )
在我看来,正确的方式是使用下表:
ID Col Val
-- --- ---
1 1 3
1 2 34
1 3 76
2 1 32
2 2 976
2 3 24
3 1 7
3 2 235
3 3 3
4 1 245
4 2 1
4 3 792
以ID/Col
为主键(可能Col
作为额外键,具体取决于您的需要)。然后,您的查询变为简单的select min(val) from tbl
,您仍然可以在其他查询中使用where col = 2
单独处理各个“旧列”。如果“旧列”的数量增加,这也可以轻松扩展。
这使您的查询如此更加容易。我倾向于使用的一般准则是,如果你曾经看起来像数据库行中的数组,你可能做错了什么,应该考虑重组数据。
但是,如果由于某种原因你无法更改这些列,我建议使用插入和更新触发器并添加另一个列,这些触发器设置为最低Col1/2/3
。这会将操作的“成本”从选择转移到它所属的更新/插入 - 我的经验中的大多数数据库表读取的频率远远超过写入,因此随着时间的推移,写入成本往往会更高效。
换句话说,一行的最小值只会在其他列之一发生变化时发生变化,所以当你应该计算它时那是,而不是每次选择时都是如此(如果数据被浪费了没有变化)。然后你会得到一个像这样的表:
ID Col1 Col2 Col3 MinVal
-- ---- ---- ---- ------
1 3 34 76 3
2 32 976 24 24
3 7 235 3 3
4 245 1 792 1
任何其他必须在select
时间做出决定的选项通常都是一个糟糕的创意,因为数据只会在插入/更新时发生变化 - 添加另一列会占用更多空间对于插入和更新来说会稍微慢一些,但对于选择可以多更快 - 首选方法应该取决于您的优先级,但如上所述,大多数表都是远比他们写的更频繁。
答案 4 :(得分:7)
你可以使用“暴力”方法:
SELECT CASE
WHEN Col1 <= Col2 AND Col1 <= Col3 THEN Col1
WHEN Col2 <= Col3 THEN Col2
ELSE Col3
END AS [Min Value] FROM [Your Table]
当第一个条件失败时,它保证Col1不是最小值,因此你可以从其他条件中消除它。同样适用于后续条件。对于五列,您的查询将变为:
SELECT CASE
WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
WHEN Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
WHEN Col3 <= Col4 AND Col3 <= Col5 THEN Col3
WHEN Col4 <= Col5 THEN Col4
ELSE Col5
END AS [Min Value] FROM [Your Table]
请注意,如果两列或多列之间存在平局,则<=
会确保我们尽早退出CASE
语句。
答案 5 :(得分:6)
如果列是整数,就像你的例子一样,我会创建一个函数:
create function f_min_int(@a as int, @b as int)
returns int
as
begin
return case when @a < @b then @a else coalesce(@b,@a) end
end
然后当我需要使用它时,我会这样做:
select col1, col2, col3, dbo.f_min_int(dbo.f_min_int(col1,col2),col3)
如果您有5个列,则上面的内容变为
select col1, col2, col3, col4, col5,
dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(col1,col2),col3),col4),col5)
答案 6 :(得分:5)
您也可以使用联合查询执行此操作。随着列数的增加,您需要修改查询,但至少它是一个直接的修改。
Select T.Id, T.Col1, T.Col2, T.Col3, A.TheMin
From YourTable T
Inner Join (
Select A.Id, Min(A.Col1) As TheMin
From (
Select Id, Col1
From YourTable
Union All
Select Id, Col2
From YourTable
Union All
Select Id, Col3
From YourTable
) As A
Group By A.Id
) As A
On T.Id = A.Id
答案 7 :(得分:5)
使用此:
select least(col1, col2, col3) FROM yourtable
答案 8 :(得分:3)
这是蛮力但有效
select case when col1 <= col2 and col1 <= col3 then col1
case when col2 <= col1 and col2 <= col3 then col2
case when col3 <= col1 and col3 <= col2 then col3
as 'TheMin'
end
from Table T
...因为min()仅适用于一列,而不适用于列。
答案 9 :(得分:2)
this question 而this question试图回答这个问题。
回顾一下,Oracle有一个内置函数,对于Sql Server,你要么定义一个用户定义的函数,要么使用case语句。
答案 10 :(得分:1)
联合查询的一点点扭曲:
DECLARE @Foo TABLE (ID INT, Col1 INT, Col2 INT, Col3 INT)
INSERT @Foo (ID, Col1, Col2, Col3)
VALUES
(1, 3, 34, 76),
(2, 32, 976, 24),
(3, 7, 235, 3),
(4, 245, 1, 792)
SELECT
ID,
Col1,
Col2,
Col3,
(
SELECT MIN(T.Col)
FROM
(
SELECT Foo.Col1 AS Col UNION ALL
SELECT Foo.Col2 AS Col UNION ALL
SELECT Foo.Col3 AS Col
) AS T
) AS TheMin
FROM
@Foo AS Foo
答案 11 :(得分:1)
如果你使用SQL 2005,你可以做这样的事情:
;WITH res
AS ( SELECT t.YourID ,
CAST(( SELECT Col1 AS c01 ,
Col2 AS c02 ,
Col3 AS c03 ,
Col4 AS c04 ,
Col5 AS c05
FROM YourTable AS cols
WHERE YourID = t.YourID
FOR
XML AUTO ,
ELEMENTS
) AS XML) AS colslist
FROM YourTable AS t
)
SELECT YourID ,
colslist.query('for $c in //cols return min(data($c/*))').value('.',
'real') AS YourMin ,
colslist.query('for $c in //cols return avg(data($c/*))').value('.',
'real') AS YourAvg ,
colslist.query('for $c in //cols return max(data($c/*))').value('.',
'real') AS YourMax
FROM res
这样你就不会迷失在这么多的运营商中:)
然而,这可能比其他选择慢。
这是你的选择......
答案 12 :(得分:1)
select *,
case when column1 < columnl2 And column1 < column3 then column1
when columnl2 < column1 And columnl2 < column3 then columnl2
else column3
end As minValue
from tbl_example
答案 13 :(得分:1)
如果你能够创建一个存储过程,它可能需要一组值,你可以调用它。
答案 14 :(得分:1)
下面我使用临时表来获取最少的几个日期。第一个临时表查询几个连接表以获取各种日期(以及查询的其他值),然后第二个临时表使用与日期列一样多的遍数获取各个列和最小日期。
这基本上类似于联合查询,需要相同数量的传递,但可能更有效(基于经验,但需要测试)。在这种情况下,效率不是问题(8,000条记录)。人们可以索引等。
--==================== this gets minimums and global min
if object_id('tempdb..#temp1') is not null
drop table #temp1
if object_id('tempdb..#temp2') is not null
drop table #temp2
select r.recordid , r.ReferenceNumber, i.InventionTitle, RecordDate, i.ReceivedDate
, min(fi.uploaddate) [Min File Upload], min(fi.CorrespondenceDate) [Min File Correspondence]
into #temp1
from record r
join Invention i on i.inventionid = r.recordid
left join LnkRecordFile lrf on lrf.recordid = r.recordid
left join fileinformation fi on fi.fileid = lrf.fileid
where r.recorddate > '2015-05-26'
group by r.recordid, recorddate, i.ReceivedDate,
r.ReferenceNumber, i.InventionTitle
select recordid, recorddate [min date]
into #temp2
from #temp1
update #temp2
set [min date] = ReceivedDate
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.ReceivedDate < [min date] and t1.ReceivedDate > '2001-01-01'
update #temp2
set [min date] = t1.[Min File Upload]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Upload] < [min date] and t1.[Min File Upload] > '2001-01-01'
update #temp2
set [min date] = t1.[Min File Correspondence]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Correspondence] < [min date] and t1.[Min File Correspondence] > '2001-01-01'
select t1.*, t2.[min date] [LOWEST DATE]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
order by t1.recordid
答案 15 :(得分:1)
对于多列,最好使用CASE语句,但对于两个数字列i和j,您可以使用简单的数学:
min(i,j)=(i + j)/ 2 - abs(i-j)/ 2
此公式可用于获取多列的最小值,但过去2,min(i,j,k)非常混乱min(i,min(j,k))
答案 16 :(得分:1)
SELECT [ID],
(
SELECT MIN([value].[MinValue])
FROM
(
VALUES
([Col1]),
([Col1]),
([Col2]),
([Col3])
) AS [value] ([MinValue])
) AS [MinValue]
FROM Table;
答案 17 :(得分:0)
案例
当 Col1 < Col2 和 Col1 < Col3
然后 Col1
当 Col2 为空且 Col3 为空时
然后 Col1
当 Col1 < Col2 且 Col3 为空时
然后 Col1
当 Col1 < Col3 且 Col2 为空时
然后 Col1
当 Col2 < Col1 和 Col2 < Col3
然后 Col2
当 Col1 为空且 Col3 为空时
然后 Col2
当 Col2 < Col1 且 Col3 为空时
然后 Col2
当 Col2 < Col3 且 Col1 为空时
然后 Col2
当 Col3 < Col1 和 Col3 < Col2
然后 Col3
当 Col1 为空且 Col2 为空时
然后 Col3
当 Col3 < Col1 且 Col2 为空时
然后 Col3
当 Col3 < Col2 且 Col1 为空时
然后 Col3
当 Col2 = Col3
然后 Col2
当 Col1 = Col3
然后 Col1
当 Col1 = Col2
然后 Col1
当 Col2 = Col3 且 Col1 = Col3
然后 Col1
否则空结尾为 'MIN'
答案 18 :(得分:0)
我知道这个问题很旧,但是我仍然需要答案,并且对其他答案不满意,因此我不得不设计自己的答案,这与@paxdiablo的answer有所不同。 / p>
我来自SAP ASE 16.0,只需要看一下IMHO有效存储在单行不同列中的某些数据的统计信息(它们表示不同的时间-计划何时到达某项内容行动开始时的期望值,最后是实际时间)。因此,我已将列转换为临时表的行,并像往常一样对此执行查询。
不是前面的万能解决方案!
CREATE TABLE #tempTable (ID int, columnName varchar(20), dataValue int)
INSERT INTO #tempTable
SELECT ID, 'Col1', Col1
FROM sourceTable
WHERE Col1 IS NOT NULL
INSERT INTO #tempTable
SELECT ID, 'Col2', Col2
FROM sourceTable
WHERE Col2 IS NOT NULL
INSERT INTO #tempTable
SELECT ID, 'Col3', Col3
FROM sourceTable
WHERE Col3 IS NOT NULL
SELECT ID
, min(dataValue) AS 'Min'
, max(dataValue) AS 'Max'
, max(dataValue) - min(dataValue) AS 'Diff'
FROM #tempTable
GROUP BY ID
这在630000行的源集上花费了大约30秒,并且仅使用索引数据,因此不是要在时间紧迫的过程中运行,而是要进行一次性数据检查或日末报告等操作您可能还不错(但请与您的同行或上级进行验证!)。 这种对我来说 的主要好处是,我可以轻松使用更多/更少的列并更改分组,过滤等,尤其是在复制了数据之后。
其他数据(columnName
,max
es,...)可以帮助我进行搜索,因此您可能不需要它们。我把它们留在这里也许是可以激发一些想法的:-)。
答案 19 :(得分:0)
如果您知道要查找的值,通常是状态代码,以下内容可能会有所帮助:
select case when 0 in (PAGE1STATUS ,PAGE2STATUS ,PAGE3STATUS,
PAGE4STATUS,PAGE5STATUS ,PAGE6STATUS) then 0 else 1 end
FROM CUSTOMERS_FORMS