SQL查询(按订单)

SQL查询(按订单),sql,sorting,Sql,Sorting,我想在我的表/关系客户中列出(排序列表)来自名为streetNames的属性的所有条目。 我想实现以下目标: 街道1A 街道1B 街道2A 街道2B 街道_12A 第12B街 按街道名称的简单顺序将进行词汇比较,然后街道12A和B将排在街道2A/B之前,这是不正确的。是否可以通过纯SQL解决此问题?从表X中选择街道名称 按udf_getStreetNumber(街道名称)订购 在你的udf_getStreetNumber中——写下你的业务规则,去掉这个数字 编辑 我认为现在可以在SQL Serv

我想在我的表/关系客户中列出(排序列表)来自名为streetNames的属性的所有条目。 我想实现以下目标:

街道1A
街道1B
街道2A
街道2B
街道_12A
第12B街


按街道名称的简单顺序将进行词汇比较,然后街道12A和B将排在街道2A/B之前,这是不正确的。是否可以通过纯SQL解决此问题?

从表X中选择街道名称 按udf_getStreetNumber(街道名称)订购

在你的udf_getStreetNumber中——写下你的业务规则,去掉这个数字

编辑


我认为现在可以在SQL Server中使用正则表达式功能。我只是从输入中去掉所有非数字字符。

是的,这是可能的!但绝对没有兴趣!如果您发现这里有人准备花几个小时写下并测试SP,该SP将把您的街道名称拆分为街道名称+街道编号组合,请告诉我他的姓名:我将向他提交一些我认为必须付费才能完成工作的问题


顺便问一下,你不能把你的数据分成两个字段,一个是“streetName”,只有街道名称,另一个是新的“buildingNumber”字段吗?(避免将此字段命名为“streetNumber”,因为在某些国家/城市,街道是给定的编号)。

我相信您可以通过将streetName字段拆分为不同的部分,例如substr(streetName,1,find(“,streetName)),仅用于街道等。但这将是相当混乱的,它将不得不处理各种特殊情况(没有门牌号,门牌号没有添加)或国际问题(在美国,地址通常像一条街)


但是,如果您希望按照您所描述的排序,并且这是一项重要的要求,那么最好将streetName分为三个部分,即street(例如“street”)、house_number(例如1、2、12)、house_num(例如“A”、“B”)。在SQL中,排序变得很简单。

如果您有对数据库的写访问权,我真的建议您将其全部转换为使用3个单独的字段,然后适当地使用它们。通过这种方式,您甚至可以在PHP中完成(是的,这需要一些时间,但只会发生一次)。 如果您有一个庞大的代码库,必须检查此表中的所有查询,那么这可能会有些痛苦,但最终会得到回报。例如,它将使按地址搜索变得更加容易。

可靠的方法是将数据拆分为街道名称和门牌号,并单独对它们进行排序(可靠的方法是“正确排序数据”,而不是“解决一般问题”)。但这需要知道门牌号从哪里开始。这是一个棘手的部分——让假设最适合你的数据

您应该使用以下类似的方法来重构数据,并从现在起将门牌号存储在单独的字段中。在对大型数据集进行排序时,所有这些字符串杂耍都不会表现得太好

假设它是街道名称中的最后一项,并且包含一个数字:

DECLARE @test TABLE
(
  street VARCHAR(100)
)

INSERT INTO @test (street) VALUES('Street')
INSERT INTO @test (street) VALUES('Street 1A')
INSERT INTO @test (street) VALUES('Street1 12B')
INSERT INTO @test (street) VALUES('Street 22A')
INSERT INTO @test (street) VALUES('Street1 200B-8a')
INSERT INTO @test (street) VALUES('')
INSERT INTO @test (street) VALUES(NULL)

SELECT
  street,
  CASE 
    WHEN LEN(street) > 0 AND CHARINDEX(' ', REVERSE(street)) > 0
    THEN CASE
      WHEN RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1) LIKE '%[0-9]%'
      THEN LEFT(street, LEN(street) - CHARINDEX(' ', REVERSE(street)))
    END
  END street_part,
  CASE 
    WHEN LEN(street) > 0 AND CHARINDEX(' ', REVERSE(street)) > 0
    THEN CASE 
      WHEN RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1) LIKE '%[0-9]%'
      THEN RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1)
    END
  END house_part,
  CASE 
    WHEN LEN(street) > 0 AND CHARINDEX(' ', REVERSE(street)) > 0
    THEN CASE 
      WHEN RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1) LIKE '%[0-9]%'
      THEN CASE
        WHEN PATINDEX('%[a-z]%', LOWER(RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1))) > 0
        THEN CONVERT(INT, LEFT(RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1), PATINDEX('%[^0-9]%', LOWER(RIGHT(street, CHARINDEX(' ', REVERSE(street)) - 1))) - 1))
      END
    END
  END house_part_num
FROM
  @test 
ORDER BY
  street_part,
  house_part_num,
  house_part
这假设了以下条件:

  • 街道地址可以有门牌号
  • 门牌号必须是街道地址中的最后一项(编号“525 Monroe Av”)
  • 门牌号应以正确排序的数字开头
  • 一个门牌号可以是一个范围(“200-205”),这将在200以下排序
  • 门牌号不得包含空格或识别失败(当您查看数据时,您可以应用类似于
    REPLACE(street,'-','-')
    的方法事先清理常见模式。)
  • 整个过程仍然是一个近似值,当然与电话簿中的情况不同

如果streetNames列中的所有值都遵循该模式 街道名称-空间-街道编号

如果StreetName可以包含其他空格,但StreetNumber不能,则这将起作用:

Declare @T Table (streetName VarChar(50))
Insert @T(streetName) Values('Street 1A')
Insert @T(streetName) Values('Street 2A')
Insert @T(streetName) Values('Street 2B')
Insert @T(streetName) Values('Street 12A')
Insert @T(streetName) Values('Another Street 1A')
Insert @T(streetName) Values('Another Street 4A')
Insert @T(streetName) Values('a third Street 12B')
Insert @T(streetName) Values('a third Street 1C')

Select * From @T 
Order By Substring(StreetName, 0, 1 + len(StreetName) - charIndex(' ', reverse(StreetName))),
       Cast(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)),  
        Case When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 5)) = 1  Then 5
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 4)) = 1  Then 4
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 3)) = 1  Then 3
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 2)) = 1  Then 2
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 1)) = 1  Then 1
                End) as Integer),
        Substring(StreetName, len(StreetName) - charIndex(' ', reverse(StreetName)) +
            Case When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 5)) = 1  Then 5
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 4)) = 1  Then 6
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 3)) = 1  Then 5
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 2)) = 1  Then 4
             When IsNumeric(Substring(StreetName, 2 + len(StreetName) - charIndex(' ', reverse(StreetName)), 1)) = 1  Then 3
                End, Len(StreetName))

作为记录:它被称为自然排序顺序,并且有一个 在这个问题上


我想您可以使用这里显示的一些代码在SQL中实现这一点,但它总是在一个具体的场景中实现。

什么DBMS?有些提供了更改排序顺序的非标准方法无论数据库管理系统是什么,尝试在不将数据拆分到多个字段的情况下解决此问题(如一些答案中所建议的)都是失败的原因。这将对任何超过几百行的数据运行都很糟糕。。。最好分割字段,即使它是一个使用相同功能的计算列,该功能可以持久化和索引数据是什么。。。他没有要求重新设计他的方案。我不得不在数百万行上使用这样的udf,它工作得很快而且没有问题。@mson,在数百万行上运行udf是一个真正的性能问题,除非您使用的是“内联”udf。。。这是因为(内联udf除外)每次执行都必须重新编译udf(如果在一百万行上运行它,则需要一百万次选择…@Charles-除了标量udf之外,这个商业案例如何编写?是什么让你认为这个数字是一个门牌号,而不是像他在问题中所说的那样,街道名称的一部分?街道不能轻易分解为3个部分。你会发现,国际、乡村路线、军事和疯狂街道名称的组合打破了你的规则。我认为你假设的数量限制不会超过前10行!因为你会有没有数字的行,有数字的行,有数字+文本的行(A,B,C),数字将在字段开头的行,等等…有数字的行,比如292-296等等。整个问题的唯一兴趣是通过文章的链接得到你的答案。非常感谢。