Непосредственный запрос UNION в MS Access (Jet/ACE)
Это работает как ожидалось:
SELECT "Mike" AS FName
Это не удается с ошибкой "Ввод запроса должен содержать хотя бы одну таблицу или запрос":
SELECT "Mike" AS FName
UNION ALL
SELECT "John" AS FName
Является ли это просто причудой/ограничением механизма базы данных Jet/ACE или я что-то упускаю?
Ответы
Ответ 1
Ты ничего не забыл. Механизм доступа к базе данных допускает одну строку SELECT
без источника данных FROM
. Но если вы хотите, чтобы UNION
или UNION ALL
несколько строк, вы должны включить FROM
... даже если вы не ссылаетесь на какое-либо поле из этого источника данных.
Я создал таблицу с одной строкой и добавил ограничение проверки, чтобы гарантировать, что она всегда будет иметь одну и только одну строку.
Public Sub CreateDualTable()
Dim strSql As String
strSql = "CREATE TABLE Dual (id COUNTER CONSTRAINT pkey PRIMARY KEY);"
Debug.Print strSql
CurrentProject.Connection.Execute strSql
strSql = "INSERT INTO Dual (id) VALUES (1);"
Debug.Print strSql
CurrentProject.Connection.Execute strSql
strSql = "ALTER TABLE Dual" & vbNewLine & _
vbTab & "ADD CONSTRAINT there_can_be_only_one" & vbNewLine & _
vbTab & "CHECK (" & vbNewLine & _
vbTab & vbTab & "(SELECT Count(*) FROM Dual) = 1" & vbNewLine & _
vbTab & vbTab & ");"
Debug.Print strSql
CurrentProject.Connection.Execute strSql
End Sub
Эта таблица Dual
полезна для запросов, таких как:
SELECT "foo" AS my_text
FROM Dual
UNION ALL
SELECT "bar"
FROM Dual;
Другим подходом, который я видел, является использование оператора SELECT
с предложением TOP 1
или WHERE
, которое ограничивает набор результатов одной строкой.
Ограничения проверки заметок были добавлены в Jet 4 и доступны только для операторов, выполняемых из ADO. CurrentProject.Connection.Execute strSql
работает, потому что CurrentProject.Connection
- это объект ADO. Если вы попытаетесь выполнить один и тот же оператор с DAO (т.е. CurrentDb.Execute
или из конструктора запросов Access), вы получите синтаксическую ошибку, поскольку DAO не сможет создать контрольные ограничения.
Ответ 2
Если у вас есть доступ к некоторым системным таблицам, вы можете эмулировать двойную таблицу следующим образом:
(SELECT COUNT(*) FROM MSysResources) AS DUAL
К сожалению, я не знаю никаких системных таблиц, которые...
- всегда доступны, доступны для чтения (MSysObjects может быть недоступен для каждого подключения)
- содержат ровно одну запись, такую как Oracle
DUAL
или DB2 SYSIBM.DUAL
Итак, вы должны написать:
SELECT 'Mike' AS FName
FROM (SELECT COUNT(*) FROM MSysResources) AS DUAL
UNION ALL
SELECT 'John' AS FName
FROM (SELECT COUNT(*) FROM MSysResources) AS DUAL
Это то, что реализуется как синтаксический элемент в jOOQ, например.
Ответ 3
Если вы ограничили доступ только для чтения к базе данных (т.е. вы не можете создавать новые таблицы или получать доступ к системным ресурсам), это может работать:
SELECT "Mike" AS FName
FROM (SELECT COUNT(*) FROM anyTable WHERE 1=0) AS dual
-
anyTable - это первая таблица пользователей, которую вы найдете (я не могу представить себе реальную базу данных без таблицы пользователя!).
-
WHERE 1 = 0 должен возвращать быстрый счет 0, даже на большой таблице (надеюсь, что двигатель Jet достаточно умен, чтобы распознать такое тривиальное условие).
Ответ 4
Если кто-то хочет использовать метод Top 1, он будет выглядеть так:
SELECT first_name AS FName
FROM tblname
UNION ALL
SELECT "Mike" as Fname
FROM (Select Top 1 Count(*) FROM tblsometable);
Псевдоним для поля должен быть одним и тем же с обеих сторон объединения, в данном случае "FName".
Ответ 5
Здесь гораздо более простой способ сделать это:
SELECT 'foo', 'boo', 'hoo' from TableWith1Row
union
SELECT 'foo1', 'boo1', 'hoo1' from TableWith1Row
Важно: TableWith1Row может быть таблицей с буквально 1 записью (которую вы игнорируете в любом случае) ИЛИ она может быть таблицей с любым количеством строк (должна иметь значение AT LEAST 1 row), но вы добавляете предложение WHERE, чтобы обеспечить 1 строку, Это немного бесполезно-goosy, но это быстрый способ сделать эту работу без создания дополнительных таблиц.