SQL中的ORDER BY,其中列是具有嵌入式整数的合成字符串

时间:2018-11-27 19:46:29

标签: sql sql-server h2

我有下表:

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中正确地确定薪水?

8 个答案:

答案 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