我有下表:
CREATE TABLE TEST
(
name VARCHAR(10),
date_of_entry DATE,
flag1 INT,
flag2 INT,
salary FLOAT,
flag3 INT,
id INT
);
具有以下行:
name date_of_entry flag1 flag2 salary flag3 id
--------------------------------------------------------------
AGMA 2018-11-08 0 1 265466940 1 1
AGMA 2018-11-08 0 1 220737125 1 2
AGMA 2018-11-08 0 1 181270493 0 3
AGMA 2018-11-08 0 1 8584205 0 4
我想执行以下SQL以特定方式对行进行排序:
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
但是,薪水列在字符串中的排序不正确。所以我的最终结果是(在Microsoft SQL Server中执行时):
SYNTHETIC_ORDER
-----------------------------------
AGMA.20181108.0.1.8.58421e+006.0.4
AGMA.20181108.0.1.2.65467e+008.1.1
AGMA.20181108.0.1.2.20737e+008.1.2
AGMA.20181108.0.1.1.8127e+008.0.3
可以注意到,当我希望id 1首先出现时,结果是id 4首先出现。
预期结果:
SYNTHETIC_ORDER
-----------------------------------
AGMA.20181108.0.1.2.65467e+008.1.1
AGMA.20181108.0.1.2.20737e+008.1.2
AGMA.20181108.0.1.1.8127e+008.0.3
AGMA.20181108.0.1.8.58421e+006.0.4
是否有一种方法可以确保在此SQL中正确地确定薪水?
答案 0 :(得分:3)
为什么不能仅按各列排序?
SELECT
date_of_entry, flag1, flag2, salary, flag3
, name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY date_of_entry DESC, flag1 DESC, flag2 DESC, salary DESC, flag3 DESC
这将为您带来最大的收获。
SELECT SYNTHETIC_ORDER
FROM (
SELECT
ROW_NUMBER() OVER(ORDER BY date_of_entry DESC, flag1 DESC, flag2 DESC, salary DESC, flag3 DESC) AS RowNum
, name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
) a
WHERE RowNum = 1
答案 1 :(得分:2)
这将为您提供所需的信息,但可能不会以您喜欢的方式使子查询有趣
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary / (SELECT MIN(salary) AS min_sal FROM TEST))
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY SYNTHETIC_ORDER DESC
答案 2 :(得分:2)
固定宽度表示,它仅使用H2(未标记)和SQLS(已标记)中可用的功能:
SELECT
CONCAT(
CAST(name as CHAR(10)), --right pad to 10,
YEAR(date_of_entry),
RIGHT(CONCAT('0',MONTH(date_of_entry)),2),
RIGHT(CONCAT('0',DAY(date_of_entry)),2), --yyyymmdd
CAST(flag1 as CHAR(1)), --rpad to 1, doesn't need cast if never null/0 length
CAST(flag2 as CHAR(1)), --maybe doesn't need cast, see above
RIGHT(CONCAT('0000000000', CAST(salary AS INT)),10), --lpad with 0 to 10 wide
CAST(flag3 as CHAR(1)), --maybe doesn't need cast, see above
RIGHT(CONCAT('0000000000', id), 10) --lpad with 0 to 10 wide
) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
要点:
您的CREATE TABLE语句未提及ID,但您的查询却提及ID;包含的ID
您的查询中没有提到NAME,但您的示例数据输出中有提及;包括了NAME
您可能不需要那么多填写身份证或薪水
某些字符(例如,在标志列上)的强制类型转换可以删除(如果标志列100%保证始终为1个字符长)
如果表中的薪水最大值大于一个int可以容纳的最大值,请考虑将其强制转换为其他内容
通过用前导零填充薪水,可以得出排序。如果将所有值都填充到相同的宽度,则将其归一化为0到1也是可行的,但是您可能会遇到这样的问题,即精度损失(将10位数的工资除以0.123456)将导致两种不同的工资合并,因为没有足够的数字来完整表示。使用任何量化到较低精度的除法运算,您都将冒着对原始值进行错误排序的风险(例如,如果工资分别为1000000000和1000000001且id为2和1的两个值均均归一化为0.123456,则最终会导致排序错误)。您可能需要与除法一开始的薪水一样多的位数来回答除法问题,将其填充到固定宽度,但是如果您走得那么远,则最好将所有薪水都垫到最宽的宽度上或将其全部包含在内的某个宽度。在这种情况下,如果int会溢出,则使用强制转换为int可能会很方便。您可以决定将其填充到比int所能容纳的宽度宽一位,然后如果有人插入大将来的值,并且由于溢出而导致查询开始失败,至少不会默默地提供错误的结果,因为填充是从左手边砍掉数字。在对强制转换为位进行寻址时,您可以选择是否添加一些填充的逻辑到t的LENGTH()他是SELECT MAX薪水的字符串形式
CONCAT是个不错的cos,您可以将大多数类型传递给它,而无需先强制转换为varchar,并且如果将concat设置为null也不会使整个内容为null,这与使用+或|||
的常规字符串concat ops不同答案 3 :(得分:1)
您可以尝试:
SELECT name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CHAR(DIGITS(salary))
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY SYNTHETIC_ORDER DESC
答案 4 :(得分:1)
您可以将查询更改为
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
salary DESC
答案 5 :(得分:1)
如果您需要“最大”合成订单,只需执行以下操作:
select top (1) name, date_of_entry, falg1, flag2, salary, flag3, id
from test
order by name desc, date_of_entry desc, flag1 desc, flag2 desc, salary desc, flag3 desc, id desc;
我看不出将这些值填充到字符串中的原因。
如果您想为每个name
单独放置一行,则:
select top (1) with ties name, date_of_entry, falg1, flag2, salary, flag3, id
from test
order by row_number() over (partition by name desc order by date_of_entry desc, flag1 desc, flag2 desc, salary desc, flag3 desc, id desc);
答案 6 :(得分:1)
先生,
将其转换为巨大的字符串(varchar)后,它将遵循字母顺序,而不是您期望的大小顺序。
您不能仅将row_number用作“合成订单”。换句话说,代替这个:
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
此:
SELECT
id,
row_number() over (order by date_of_entry,flag1,flag2,salary,flag3,id) as SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
祝你好运!
答案 7 :(得分:1)
尝试使用以下例程。
在您的代码中,您写CONVERT(varchar(2555), salary)
。这是行不通的,因为当您使用convert将浮点数转换为字符串时,结果的排序顺序与浮点数的排序顺序不同。例如3 < 20
,但'20' < '3'
。
例程FloatToSortable
解决了该问题。如果您在例程中传递一堆浮点数,然后对结果进行排序,您将获得与对浮点数排序相同的顺序。例如FloatToSortable(3) < FloatToSortable(20)
。
所以在您编写的代码中,
CONVERT(varchar(2555), salary)
替换为
dbo.FloatToSortable(salary).
您说您无法向数据库添加功能。那真不幸。我只是在这里使用函数来避免重复。您当然可以使用相同的前提来创建将给出相同结果的单个表达式,尽管该表达式将更长且更难于理解。
-- FloatToSortable takes a FLOAT parameter and returns a string
-- such that the sort order of FLOATs X and Y will match the
-- sort order of strings F(X) and F(Y).
--
-- The internal format of FLOAT is an 8-byte double-precision
-- float, starting with the SIGN where 0=positive and 1=negative,
-- followed by the EXPONENT and then the MANTISSA.
-- If it weren't for the SIGN we could just sort by the binary
-- value. Because of the sign we need to XOR the binary
-- before we can sort on it.
--
-- If the parameter is positive, XOR with 8000000000000000
-- If the parameter is negative, XOR with FFFFFFFFFFFFFFFF
--
-- Then we convert each byte to a Sortable string. We could
-- use hexidecimal, but it's simpler just use letters A..O
--
-- This function is working with salaries, so we don't have
-- to worry about NANs and Infinities, but it should work
-- with all values.
-- NybbleToSortable
-- Given an integer in range 0..15 return a character
-- We just map the number to a letter, 0 -> 'A', 15 -> 'O'
create function NybbleToSortable ( @a tinyint )
returns varchar(16)
as
begin
return char(@a + ascii('A'))
end
go
-- XorToSortable
-- Take the nth byte of @a, XOR it with the nth byte of @b,
-- and convert that byte to a Sortable string.
create function dbo.XorToSortable ( @a varbinary(8),
@b varbinary(8),
@n int )
returns varchar(16)
as
begin
declare @aa tinyint, @bb tinyint, @x tinyint
set @aa = cast ( substring ( @a, @n, 1 ) as tinyint )
set @bb = cast ( substring ( @b, @n, 1 ) as tinyint )
set @x = @aa ^ @bb
return dbo.NybbleToSortable ( @x / 16 )
+ dbo.NybbleToSortable ( @x % 16 )
end
go
create function dbo.FloatToSortable ( @x float )
returns varchar(16)
as
begin
declare @m varbinary(8), @b varbinary(8)
set @b = cast(@x as varbinary(8))
if @x < 0
set @m = 0xFFFFFFFFFFFFFFFF
else
set @m = 0x8000000000000000
return dbo.XorToSortable ( @b, @m, 1 )
+ dbo.XorToSortable ( @b, @m, 2 )
+ dbo.XorToSortable ( @b, @m, 3 )
+ dbo.XorToSortable ( @b, @m, 4 )
+ dbo.XorToSortable ( @b, @m, 5 )
+ dbo.XorToSortable ( @b, @m, 6 )
+ dbo.XorToSortable ( @b, @m, 7 )
+ dbo.XorToSortable ( @b, @m, 8 )
end
go
-- Create some test data
create table dbo.sal ( salary float, salbin as dbo.FloatToSortable(salary)) ;
go
declare @x float
set @x = pi()/9876543
while abs(@x) < 170
begin
insert into sal ( salary ) values ( @x )
set @x=@x * -2.014159265
end
select * from sal order by salbin
-- result is:
-- salary salbin
-- ---------------------- ----------------
-- -51.6508818660658 DPLGCMKPOHCLNIAP
-- -12.7318092715982 DPNGIJFAELIPCGOM
-- -3.1383581745746 DPPGOEKEHICIIKOI
-- -0.773597202236665 EABHDOLBBEIDLJLO
-- -0.190689716730473 EADHJHHKLHHKMEDG
-- -0.0470045237516562 EAFHOPAFOHHBPGCJ
-- -0.0115864939704268 EAHIEFFHBKJCNPMF
-- -0.00285604090440349 EAJIJKHCLHAGILBG
-- -0.000704006722693307 EALIOOFNBDCOOAMG
-- -0.000173535842863177 EANJEBBKHFNPDPAD
-- -4.27761380502506E-05 EAPJJCKPBGJEEFHA
-- -1.0544207791913E-05 EBBJODBPBNKKNIPE
-- -2.59912004745334E-06 EBDKDCGOKEJGGCDL
-- -6.4067639356036E-07 EBFKIAKBLGBGEJKE
-- 3.180862629353E-07 LOJFFIKOCNMOIIKB
-- 1.29042429395639E-06 LOLFKGFEIEBGJGMI
-- 5.23504172442538E-06 LONFPFBFEPNNJAIF
-- 2.12377138161667E-05 LOPGEEPEJEJMLAHP
-- 8.61579547748313E-05 LPBGJFPGGEGGLLMK
-- 0.000349528825712453 LPDGOIBOOABNBNJK
-- 0.00141798166313501 LPFHDLHCDHKFMEBP
-- 0.00575252124882327 LPHHIPPEKKCBMBFH
-- 0.0233370441794017 LPJHOFKKIGCELCJB
-- 0.094674597011311 LPLIDMJICJOMPBIA
-- 0.384079459692908 LPNIJEMCADJMJBKO
-- 1.55814797226306 LPPIOOCMJBHDCNED
-- 6.32115319420792 MABJEINMGCAIIEAO
-- 25.6438916046025 MADJKENGBEIHOPME
-- 104.033102255957 MAFKACBOFIOMLAIO