我在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:下一步是使用自联接将源列再次添加到结果中,但我首先需要解决上述问题。
答案 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
上的TestTable
和Choice
上的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)使用已编入索引的唯一键时非常有用。