0

我知道这个问题已经被问过好几次了,我已经阅读了所有的答案,但似乎没有一个能完全解决我的问题。

我正在从 mySQL 数据库切换到 MS Access 数据库。在这两种情况下,我都使用 php 脚本连接到数据库并执行 SQL 查询。

我需要为以前在 mySQL 上执行的查询找到合适的替代品。我想要:

  1. 执行第一个查询并根据其中一个列按字母顺序排列记录
  2. 构造一个反映先前字母顺序的 ID 列表
  3. 使用与 ID 列表一起应用并按此列表排序的 IN 子句执行第二个查询。

在 mySQL 中,我曾经以这种方式执行最后一个查询:

SELECT name FROM users WHERE id IN ($name_ids) ORDER BY FIND_IN_SET(id,'$name_ids')

由于FIND_IN_SET仅在 mySQL中可用CHARINDEX并且PATINDEX在我的 php 脚本中不可用,我该如何实现呢?

我知道我可以写如下内容:

SELECT name 
  FROM users 
 WHERE id IN ($name_ids) 
ORDER BY CASE id
           WHEN ... THEN 1 
           WHEN ... THEN 2
           WHEN ... THEN 3
           WHEN ... THEN 4
         END

但你必须考虑到:

  • ID 的列表具有可变长度和元素,因为它取决于第一个查询
  • 该列表可以轻松包含数千个元素

你对此有什么暗示吗?有没有办法以编程方式构造ORDER BY CASE ... WHEN ...语句?由于我的 ID 列表可能很大,有没有更好的方法?

更新:我执行两个单独的查询,因为我需要访问两个不同的表。数据库不是很简单,所以我试着举个例子:假设我有一个包含用户列表的表和一个包含每个用户书架中所有书籍的表。

由于数据库是在 mySQL 中设计的,因此对于每条图书记录,我将 user_id 存储在图书表中,以便在用户和图书之间建立关系。

现在假设我想获取所有用户的列表,这些用户的书籍的标题以字母“a”开头,并且我想根据书籍的字母顺序对用户进行排序。这就是我所做的:

  1. 执行第一个查询以查找所有以字母“a”开头的书籍并按字母顺序排序
  2. 创建一个 user_id 列表,该列表应反映书籍的字母顺序
  3. 在 users 表中执行查询以找出用户名并使用 user_id 列表对它们进行排序以按书籍进行所需的排序

希望这能澄清我的需要。

4

3 回答 3

2

如果我理解正确,您将尝试按照您指定 ID 值的顺序获取一组信息。有一个 hack 可以使用 XML 和 CROSS APPLY 将列表转换为表。这可以与 ROW_NUMBER 函数结合以生成您的排序顺序。请看下面的代码:

CREATE FUNCTION [dbo].[GetNvarcharsFromXmlArray] 
(   
    @Strings xml = N'<ArrayOfStrings/>'
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT     ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RowNumber, Strings.String.value('.', 'nvarchar(MAX)') AS String
    FROM          @Strings.nodes('/ArrayOfStrings/string/text()') AS Strings(String)
)

具有以下结构的函数:

<ArrayOfStrings>
    <string>myvalue1</string>
    <string>myvalue2</string>
</ArrayOfStrings>

它也是相同格式的 .NET xml 序列化字符串数组。

如果你想传递一个逗号分隔的列表,你可以简单地使用:

CREATE FUNCTION [dbo].[GetNvarcharsCSV] 
(   
    @CommaSeparatedStrings nvarchar(MAX) = N''
)
RETURNS TABLE 
AS
RETURN 
(
        DECLARE @Strings xml
        SET @Strings = CONVERT(xml, N'<ArrayOfStrings><string>' + REPLACE(@CommaSeperatedStrings, ',', N'</string><string>') + N'</string></ArrayOfStrings>')

    SELECT     ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RowNumber, Strings.String.value('.', 'nvarchar(MAX)') AS String
    FROM          @Strings.nodes('/ArrayOfStrings/string/text()') AS Strings(String)
)

这使您的查询:

SELECT name
FROM users
INNER JOIN dbo.GetNvarcharsCSV(@name_ids) AS IDList ON users.ID = IDList.String
ORDER BY RowNumber

请注意,如果您需要,使函数返回一个整数表是一个非常简单的重写。

您可以查看xml 数据类型方法,以更好地了解您可以在 SQL 查询中使用 XML 做什么。另请参阅ROW_NUMBER (Transact-SQL)

于 2012-03-23T23:18:06.563 回答
1

听起来你需要一个JOIN......
应该可以工作,尽管它可能需要翻译成 Access 语法(这显然是微妙的不同):

SELECT b.name, a.title
FROM book as a
JOIN user as b
ON b.id = a.userId
WHERE SUBSTRING(LOWER(a.title), 1, 1) = 'a'
ORDER by a.title

我不知道您为什么要切换到 Access,尽管我听说它近年来一直在改进。不过,我想我更喜欢几乎任何其他 RDBMS。从事物的声音来看,您的架构可能会受到一些调整。

于 2012-03-26T15:59:00.940 回答
0

您必须使用用户定义的函数来维护顺序,然后按该列排序。例如:

CREATE FUNCTION dbo.SplitList
(
    @List VARCHAR(8000)
)
RETURNS TABLE
AS
  RETURN
  (
    SELECT DISTINCT
      [Rank],
      [Value] = CONVERT(INT, LTRIM(RTRIM(SUBSTRING(@List, [Rank],
        CHARINDEX(',', @List + ',', [Rank]) - [Rank]))))
    FROM
    (
      SELECT TOP (8000) [Rank] = ROW_NUMBER()
        OVER (ORDER BY s1.[object_id])
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
    ) AS n
    WHERE [Rank] <= LEN(@List)
      AND SUBSTRING(',' + @List, [Rank], 1) = ','
);
GO

现在您的查询可能如下所示:

SELECT u.name 
  FROM dbo.users AS u 
  INNER JOIN dbo.SplitList($name_ids) AS s
  ON u.id = s.Value
  ORDER BY s.[Rank];

根据 SQL 语句的构造方式,您可能必须$name_ids用单引号 ( ) 括起来。dbo.SplitList('$name_ids')您可能需要考虑使用存储过程而不是在 PHP 中构建此查询。

您也可以考虑完全跳过 MS-Access 作为一个跳跃点。为什么不让 PHP 直接与 SQL Server 通信?

于 2012-03-23T23:27:32.193 回答