Access 2007 выберите первое значение результатов запроса - PullRequest
2 голосов
/ 15 марта 2012

Я сталкиваюсь с довольно раздражающей вещью в Access (2007), и я не уверен, является ли это функцией или я прошу невозможного.

Хотя фактическая структура базы данных более сложная,Моя проблема сводится к следующему:

У меня есть таблица с данными о единицах за определенные годы.Эти данные поступают из разных источников и могут перекрываться.

Unit | IYR  | X1   | Source | 
-----------------------------
A    | 2009 |  55  |     1  | 
A    | 2010 |  80  |     1  | 
A    | 2010 | 101  |     2  | 
A    | 2010 | 150  |     3  | 
A    | 2011 |  90  |     1  | 
...

Теперь я бы хотел, чтобы пользователь выбрал определенные источники, упорядочил их по приоритету и затем извлек одно значение данных за каждый год.Например, если пользователь выбирает источник 1, 2 и 3 и упорядочивает их по (3, 1, 2), то мне нужен следующий результат:

Unit | IYR  | X1   | Source | 
-----------------------------
A    | 2009 |  55  |     1  | 
A    | 2010 | 150  |     3  | 
A    | 2011 |  90  |     1  | 

Я могу заказать исходную таблицуна основе конкретного заказа.Я делаю это с помощью следующего запроса

SELECT Unit, IYR, X1, Source
FROM TestTable
WHERE Source In (1,2,3)
ORDER BY Unit, IYR,
IIf(Source=3,1,IIf(Source=1,2,IIf(Source=2,3,4)))

Это дает мне следующий промежуточный результат:

Unit | IYR  | X1   | Source | 
-----------------------------
A    | 2009 |  55  |     1  | 
A    | 2010 | 150  |     3  | 
A    | 2010 |  80  |     1  | 
A    | 2010 | 101  |     2  | 
A    | 2011 |  90  |     1  | 

Следующим шагом является получение только первого значения каждого года.Я думал использовать следующий запрос:

SELECT X.Unit, X.IYR, first(X.X1) as FirstX1
FROM (...) AS X
GROUP BY X.Unit, X.IYR

Где (…) - вышеупомянутый запрос.

Теперь Access идет бананом.Какой бы порядок я ни дал промежуточным результатам, результат этого запроса будет.

Unit | IYR  | X1   | 
--------------------
A    | 2009 |  55  | 
A    | 2010 |  80  | 
A    | 2011 |  90  | 

Другими словами, для 2010 года он показывает значение источника 1 вместо 3. Кажется, что Access не заботится о порядке вложенного запроса, когда он применяет функцию FIRST () ипридерживается оригинального порядка данных.

Это особенность Access или есть другой способ достижения желаемых результатов?

Ps: Следующим шагом будет использование самостоятельного объединения для добавленияИсходный столбец с результатами снова, но сначала мне нужно решить вышеуказанную проблему.

Ответы [ 3 ]

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

Да, FIRST() использует произвольный порядок.Из справки Access :

Эти функции возвращают значение указанного поля в первой или последней записи, соответственно, набора результатов, возвращенного запросом.Если запрос не включает предложение ORDER BY, значения, возвращаемые этими функциями, будут произвольными, поскольку записи обычно возвращаются в произвольном порядке.

Я не знаю, означает ли FROM (...) AS Xиспользуете ORDER BY inline (при условии, что это действительно возможно) или если вы используете VIEW ('сохраненный объект запроса') здесь, но в любом случае я предполагаю, что ORDER BY игнорируется (поскольку ORDER BYприменяется только к окончательному результату).

Альтернативой является использование MIN() (или, возможно, MAX()).

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

Вместо того, чтобы использовать сначала, может быть лучше определить приоритет MIN, а затем присоединиться, например.

SELECT 
       t.UNIT,
       t.IYR,
       t.X1,
       t.Source ,
       t.PrioritySource 
FROM
       (SELECT 
              Unit, 
              IYR, 
              X1, 
              Source,
              SWITCH (  [Source]=3,     1,
                        [Source]=1,     2,
                        [Source]=2,     3) as PrioritySource 
       FROM 
              TestTable
       WHERE 
              Source In (1,2,3)
       ) as t

       INNER JOIN 

       (SELECT 
              Unit, 
              IYR,
              MIN(SWITCH (    [Source]=3,     1,
                              [Source]=1,     2,
                              [Source]=2,     3)) as PrioritySource 

       FROM 
              TestTable
       WHERE 
              Source In (1,2,3)
       GROUP BY 
              Unit, 
              IYR ) as MinPriortiy

       ON t.Unit = MinPriortiy.Unit and
          t.IYR = MinPriortiy.IYR and
          t.PrioritySource = MinPriortiy.PrioritySource

, который будет давать этот результат (Примечание. Я включаю источник и источник приоритета только для демонстрационных целей)

UNIT | IYR  | X1  | Source | PrioritySource
----------------------------------------------
A    | 2009 | 55  | 1      | 2
A    | 2010 | 150 | 3      | 1
A    | 2011 | 90  | 1      | 2

Обратите внимание, что первый подзапрос обрабатывает тот факт, что Access не позволит вам присоединиться к коммутатору

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

Это самый краткий способ, которым я нашел, чтобы написать такие запросы в Access, которые требуют вытягивания всех столбцов, которые соответствуют первой строке в группе записей, которые упорядочены определенным образом.

Сначала я добавил UniqueID в вашу таблицу. В данном случае это просто поле AutoNumber. Возможно, у вас уже есть уникальное значение в вашей таблице, и в этом случае вы можете использовать это.

Сначала будет выбрана строка с источником 3, затем с источником 1, затем с источником 2. Если есть связь, выбирается строка с более высоким значением X1. Если есть дальнейшая связь, она нарушается значением UniqueID:

SELECT t.* INTO [Chosen Rows]
FROM TestTable AS t
WHERE t.UniqueID=
    (SELECT TOP 1 [UniqueID] FROM [TestTable]
        WHERE t.IYR=IYR ORDER BY Choose([Source],2,3,1), X1 DESC, UniqueID)

Это дает:

Unit    IYR     X1  Source  UniqueID
A       2009    55  1       1
A       2010    150 3       4
A       2011    90  1       5

Я рекомендую (1) создать индекс для поля IYR - это значительно повысит вашу производительность для этого типа запроса, и (2) если у вас много (> ~ 100K) записей, это не так лучший выбор. Я считаю, что это работает довольно хорошо для таблиц в диапазоне 1-70К. Для больших наборов данных я хотел бы использовать мою функцию GroupIncrement для разбиения каждой группы (аналогично SQL Server ROW_NUMBER () OVER ).

Функция Choose() является функцией VBA и может быть здесь неясной. В вашем случае это звучит так, как будто требуется некоторая интерактивность. Для этого вы можете создать вторую таблицу под названием «Выбор», например:

Rank    Choice
1       3
2       1
3       2

Затем вы можете заменить следующее:

SELECT t.* INTO [Chosen Rows]
FROM TestTable AS t
WHERE t.UniqueID=(SELECT TOP 1 [UniqueID] FROM
    [TestTable] t2 INNER JOIN [Choices] c
        ON t2.Source=c.Choice 
    WHERE t.IYR=t2.IYR ORDER BY c.[Rank], t2.X1 DESC, t2.UniqueID);

Индексирование Source для TestTable и Choice для таблицы Choices может быть полезным и здесь, в зависимости от количества требуемых вариантов выбора.


Q:

Можете ли вы заставить это работать без необходимости использования суррогатного ключа? За пример, что если уникальный ключ является составной {Unit, IYR, X1, источник}

A:

Если у вас есть составной ключ, вы можете сделать это следующим образом - однако я думаю, что если у вас большой набор данных, это полностью убьет производительность запроса. Это может помочь проиндексировать все четыре столбца, но я не могу сказать наверняка, потому что я не использую этот метод регулярно.

SELECT t.* INTO [Chosen Rows]
FROM TestTable AS t
WHERE t.Unit & t.IYR & t.X1 & t.Source =
    (SELECT TOP 1 Unit & IYR & X1 & Source  FROM [TestTable]
        WHERE t.IYR=IYR ORDER BY Choose([Source],2,3,1), X1 DESC, Unit, IYR)

В некоторых случаях вам может потребоваться объединить некоторые отдельные части ключа следующим образом (хотя Access обычно объединяет значения автоматически):

t.Unit & CStr(t.IYR) & CStr(t.X1) & CStr(t.Source)

Вы также можете использовать запрос в своих операторах FROM вместо фактической таблицы. Сам запрос создаст состав из четырех полей, используемых в ключе, и затем вы будете использовать новое имя ключа в предложении WHERE верхнего оператора SELECT и в SELECT TOP 1 [ключ] подзапроса.

В целом, я либо: (a) создаю новую таблицу с полем AutoNumber, (b) добавлю поле AutoNumber, (c) добавлю целое число и заполню его уникальным числом с помощью VBA - это полезно, когда вы получаете ошибку MaxLocks при попытке добавить AutoNumber или (d) использовать уже проиндексированный уникальный ключ.

...