Access 2007选择查询结果的第一个值

时间:2012-03-15 14:47:09

标签: sql ms-access ms-access-2007

我在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的值。当应用FIRST()函数并且坚持原始排序时,Access似乎并不关心嵌套查询的排序。数据。

这是Access的功能还是有不同的方法来实现所需的结果?

Ps:下一步是使用自联接将源列再次添加到结果中,但我首先需要解决上述问题。

3 个答案:

答案 0 :(得分:1)

不是先使用,最好确定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不允许您加入Switch

的事实

答案 1 :(得分:1)

是的,FIRST()确实使用了任意顺序。来自Access Help

  

这些函数返回第一个或第一个中指定字段的值   分别是查询返回的结果集的最后一条记录。如果   查询不包含ORDER BY子句,返回的值   这些函数将是任意的,因为通常返回记录   没有特别的顺序。

我不知道FROM (...) AS X是否表示您使用ORDER BY内联(假设实际上是可行的)或者您使用的是VIEW('存储的查询对象')但是无论哪种方式,我都认为ORDER BY被忽略了(因为ORDER BY应该只适用于最终结果)。

另一种方法是使用MIN()(或可能MAX())。

答案 2 :(得分:0)

这是我在Access中编写此类查询的最简洁方法,需要撤回与以特定方式排序的一组记录中第一行对应的所有列。

首先,我在您的表中添加了一个UniqueID。在这种情况下,它只是一个自动编号字段。您的表中可能已经有一个唯一值,在这种情况下您可以使用它。

这将首先选择Source 3,然后选择Source 1,然后选择Source 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-70K范围内的表格。对于较大的数据集,我喜欢使用GroupIncrement函数对每个组进行分区(类似于SQL Server的ROW_NUMBER() OVER语句)。

Choose()函数是VBA函数,此处可能不清楚。在您的情况下,听起来需要一些交互性。为此,您可以创建一个名为“Choices”的第二个表,如下所示:

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上的TestTableChoice上的Choices索引也可能会有所帮助,具体取决于所需的选项数量。

<小时/> 问:

  

您是否可以在不需要代理密钥的情况下使用此功能?对于   如果唯一键是复合词,该怎么办?   {单位,稻米年,X1,源}

如果你有一个复合键,你可以这样做 - 但是我认为如果你有一个大型数据集,它将完全扼杀查询的性能。它可以帮助索引所有四列,但我不能肯定地说,因为我不经常使用这种方法。

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语句中使用查询而不是实际表。查询本身将构建密钥中使用的四个字段的组合,然后您将在顶部SELECT语句的WHERE子句中以及子查询的SELECT TOP 1 [key]中使用新的密钥名称。 / p>

一般情况下,我会:(a)创建一个带有AutoNumber字段的新表,(b)添加一个AutoNumber字段,(c)添加一个整数并使用VBA用唯一的数字填充它 - 这是在尝试添加自动编号时遇到MaxLocks错误或(d)使用已编入索引的唯一键时非常有用。