我有一个奇怪的问题,由客户给我。
他有一个数据列表,括号中的日期如下:
Foo (14/08/2012)
Bar (15/08/2012)
Bar (16/09/2012)
Xyz (20/10/2012)
但是,他希望列表显示如下:
Foo (14/08/2012)
Bar (16/09/2012)
Bar (15/08/2012)
Foot (20/10/2012)
(注意第二个柱子向上移动了一个位置)
因此,它背后的逻辑是,列表必须按日期升序排序,除非两行具有相同的名称('Bar')。如果它们具有相同的名称,则必须使用最顶层的最新日期进行排序,同时保持其他排序顺序。
这甚至可以远程实现吗?我已经尝试了很多ORDER BY
条款,但找不到合适的条款。有没有人有想法?
我应该指定此数据来自sql server数据库中的表(Name和date位于两个不同的列中)。所以我正在寻找一个可以进行我想要的排序的SQL查询。
(我已经把这个例子愚蠢了,所以如果你需要更多的背景,请不要犹豫)
答案 0 :(得分:4)
这很有效,我想
declare @t table (data varchar(50), date datetime)
insert @t
values
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-09-16'),
('Xyz','2012-10-20')
select t.*
from @t t
inner join (select data, COUNT(*) cg, MAX(date) as mg from @t group by data) tc
on t.data = tc.data
order by case when cg>1 then mg else date end, date desc
生成
data date
---------- -----------------------
Foo 2012-08-14 00:00:00.000
Bar 2012-09-16 00:00:00.000
Bar 2012-08-15 00:00:00.000
Xyz 2012-10-20 00:00:00.000
答案 1 :(得分:1)
与其他任何已发布的答案相比,性能更佳的方法是完全使用ORDER BY
而不是JOIN
或使用CTE
:
DECLARE @t TABLE (myData varchar(50), myDate datetime)
INSERT INTO @t VALUES
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-09-16'),
('Xyz','2012-10-20')
SELECT *
FROM @t t1
ORDER BY (SELECT MIN(t2.myDate) FROM @t t2 WHERE t2.myData = t1.myData), T1.myDate DESC
这完全符合您的要求,并且可以与任何索引一起使用,并且与大量其他答案相比,数据量更大。
另外,你在这里尝试做的事情要清楚得多,而不是用连接的复杂性掩盖真实逻辑并检查连接项目的数量。
答案 2 :(得分:1)
这个使用analytic functions来执行排序,它只需要你表中的一个SELECT。
内部查询找到名称更改的间隙。这些间隙用于标识下一个查询中的组,外部查询按这些组进行最终排序。
我已尝试使用扩展测试数据here (SQL Fiddle)。
SELECT name, dat
FROM (
SELECT name, dat, SUM(gap) over(ORDER BY dat, name) AS grp
FROM (
SELECT name, dat,
CASE WHEN LAG(name) OVER (ORDER BY dat, name) = name THEN 0 ELSE 1 END AS gap
FROM t
) x
) y
ORDER BY grp, dat DESC
扩展测试数据
('Bar','2012-08-12'),
('Bar','2012-08-11'),
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-08-16'),
('Bar','2012-09-17'),
('Xyz','2012-10-20')
<强>结果强>
Bar 2012-08-12
Bar 2012-08-11
Foo 2012-08-14
Bar 2012-09-17
Bar 2012-08-16
Bar 2012-08-15
Xyz 2012-10-20
答案 3 :(得分:0)
我认为这是有效的,包括我在评论中询问的案例:
declare @t table (data varchar(50), [date] datetime)
insert @t
values
('Foo','20120814'),
('Bar','20120815'),
('Bar','20120916'),
('Xyz','20121020')
; With OuterSort as (
select *,ROW_NUMBER() OVER (ORDER BY [date] asc) as rn from @t
)
--Now we need to find contiguous ranges of the same data value, and the min and max row number for such a range
, Islands as (
select data,rn as rnMin,rn as rnMax from OuterSort os where not exists (select * from OuterSort os2 where os2.data = os.data and os2.rn = os.rn - 1)
union all
select i.data,rnMin,os.rn
from
Islands i
inner join
OuterSort os
on
i.data = os.data and
i.rnMax = os.rn-1
), FullIslands as (
select
data,rnMin,MAX(rnMax) as rnMax
from Islands
group by data,rnMin
)
select
*
from
OuterSort os
inner join
FullIslands fi
on
os.rn between fi.rnMin and fi.rnMax
order by
fi.rnMin asc,os.rn desc
首先计算OuterSort
CTE中的初始排序。然后,使用两个CTE(Islands
和FullIslands
),我们计算该排序的部分,其中相同的数据值出现在相邻的行中。完成后,我们可以通过所有相邻值将具有的任何值(例如它们所属的“岛”的最低行数)计算最终排序,然后在“岛”内,我们使用相反的最初计算的排序顺序。
请注意,这可能对大型数据集来说效率不高。在示例数据上,它显示为需要对基表进行4次表扫描,以及一个假脱机。
答案 4 :(得分:-2)
尝试类似......
ORDER BY CASE date
WHEN '14/08/2012' THEN 1
WHEN '16/09/2012' THEN 2
WHEN '15/08/2012' THEN 3
WHEN '20/10/2012' THEN 4
END
在MySQL中,你可以这样做:
ORDER BY FIELD(date, '14/08/2012', '16/09/2012', '15/08/2012', '20/10/2012')
在potgres中,可以创建函数FIELD
CREATE OR REPLACE FUNCTION field(anyelement, anyarray) RETURNS numeric AS $$
SELECT
COALESCE((SELECT i
FROM generate_series(1, array_upper($2, 1)) gs(i)
WHERE $2[i] = $1),
0);
$$ LANGUAGE SQL STABLE
如果您不想使用CASE,可以尝试在SQL Server中找到FIELD函数的实现。