MS Access SQL: выберите строки в том же порядке, что и предложение IN - PullRequest
0 голосов
/ 24 марта 2012

Я знаю, что этот вопрос задавался несколько раз, и я прочитал все ответы, но ни один из них, похоже, не полностью решил мою проблему.

Я переключаюсь с базы данных MySQL на MS Accessбаза данных.В обоих случаях я использую скрипт php для подключения к базе данных и выполнения SQL-запросов.

Мне нужно найти подходящую замену для запроса, который я использовал для выполнения в mySQL.Я хочу:

  1. выполнить первый запрос и упорядочить записи в алфавитном порядке на основе одного из столбцов
  2. создать список идентификаторов, который отражает предыдущий алфавитный порядок
  3. выполнить второй запрос с предложением 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

но вы должны учитывать, что:

  • список идентификаторов имеет переменную длину и элементы, потому что это зависит от первого запроса
  • , что список может легко содержать тысячи элементов

У вас есть намеки на это?Есть ли способ программно построить оператор ORDER BY CASE ... WHEN ...?Есть ли лучший подход, так как мой список идентификаторов может быть большим?

ОБНОВЛЕНИЕ: Я выполняю два отдельных запроса, потому что мне нужно получить доступ к двум разным таблицам.С базой данных это не очень просто, поэтому я попытаюсь привести пример: предположим, у меня есть таблица со списком пользователей и таблица со всеми книгами, которые есть у каждого пользователя на их книжной полке.

С базы данныхбыл разработан в mySQL, для каждой записи книги я сохраняю user_id в таблице books, чтобы иметь связь между пользователем и книгой.

Предположим теперь, что я хочу получить список всех пользователей, которыеесть книги с заголовком, начинающимся с буквы «а», и я хочу заказать пользователя на основе алфавитного порядка книг.Вот что я делаю:

  1. выполните первый запрос, чтобы найти все книги, которые начинаются с буквы «а», и отсортируйте по алфавиту
  2. , создайте список user_id, который должен отражатьВ алфавитном порядке книги
  3. выполните запрос в таблице пользователей, чтобы узнать имена пользователей и отсортировать их по списку user_id, чтобы получить требуемую сортировку по книге

Надеюсь, что это уточнитьчто мне нужно.

Ответы [ 3 ]

2 голосов
/ 24 марта 2012

Если я правильно понимаю, вы пытаетесь получить набор информации в том же порядке, в котором вы указали значения идентификатора.Существует хак, который может преобразовать список в таблицу, используя 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 Методы типов данных , чтобы лучше понять, что вы можете делать с XML в запросах SQL.Также см. ROW_NUMBER (Transact-SQL) .

1 голос
/ 26 марта 2012

Звучит так, будто вам нужно 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, хотя я слышал, что он улучшается в последние годы.Я думаю, что я предпочел бы почти любую другую СУБД.И ваша схема, вероятно, могла бы немного измениться от звука вещей.

0 голосов
/ 24 марта 2012

Вы должны будете использовать пользовательскую функцию, которая поддерживает порядок, а затем упорядочивать по этому столбцу. Например:

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];

Возможно, вам придется заключить в $name_ids одинарные кавычки (dbo.SplitList('$name_ids')) в зависимости от того, как составлен оператор SQL. Возможно, вы захотите использовать хранимую процедуру вместо построения этого запроса в PHP.

Вы могли бы также рассмотреть возможность пропуска MS-Access в качестве точки переключения вообще. Почему бы просто не связать PHP напрямую с SQL Server?

...