Запрос UNION без таблиц в MS Access (Jet/ACE)
Это работает как ожидалось:
SELECT "Mike" AS FName
Это приводит к ошибке "Ввод запроса должен содержать хотя бы одну таблицу или запрос":
SELECT "Mike" AS FName
UNION ALL
SELECT "John" AS FName
Это просто причуды / ограничения ядра базы данных Jet/ACE или я что-то упустил?
6 ответов
Вы ничего не упустили. Доступ к базе данных двигателя позволит одну строку 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 не может создавать ограничения проверки.
Если у вас есть доступ к некоторым системным таблицам, вы можете эмулировать двойную таблицу следующим образом:
(SELECT COUNT(*) FROM MSysResources) AS DUAL
К сожалению, я не знаю ни о каких системных таблицах, которые...
- всегда доступны для чтения (объекты MSysObject могут быть недоступны для каждого соединения)
- содержат ровно одну запись, например Oracle
DUAL
или DB2SYSIBM.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, например.
Если у вас ограничен доступ только для чтения к базе данных (т.е. вы не можете создавать новые таблицы или обращаться к системным ресурсам), это может сработать:
SELECT "Mike" AS FName
FROM (SELECT COUNT(*) FROM anyTable WHERE 1=0) AS dual
anyTable - первая найденная вами таблица пользователя (я с трудом могу представить реальную базу данных без таблицы пользователей!).
ГДЕ 1=0 должен быстро возвращать счет 0 даже на большом столе (надеюсь, что реактивный двигатель достаточно умен, чтобы распознавать такие тривиальные условия).
Вот гораздо более простой способ сделать это:
SELECT 'foo', 'boo', 'hoo' from TableWith1Row
union
SELECT 'foo1', 'boo1', 'hoo1' from TableWith1Row
Важное замечание: TableWith1Row может ЛИБО быть таблицей с буквально 1 записью (которую вы в любом случае игнорируете) ИЛИ это может быть таблица с любым количеством строк (должно быть по крайней мере 1 строка), но вы добавляете предложение WHERE для обеспечения 1 строки. Это немного непросто, но это быстрый способ сделать это без создания дополнительных таблиц.
Если кто-то хочет использовать метод Top 1, он будет выглядеть так:
SELECT first_name AS FName
FROM tblname
UNION ALL
SELECT "Mike" as Fname
FROM (Select Top 1 Count(*) FROM tblsometable);
Псевдоним для поля должен быть одинаковым на обеих сторонах объединения, в данном случае "FName".
Введите любое имя таблицы (вам не нужно выбирать столбец из нее).
Этот запрос дает мне 3 финансовых года, которые мне нужны для выпадающего списка. Финансовый год начинается в июле.
SELECT IIf(Month(Now())>6,Year(Now())-1,Year(Now())-2) AS FY
FROM table
UNION
SELECT IIf(Month(Now())>6,Year(Now()),Year(Now())-1) AS FY
FROM table
UNION
SELECT IIf(Month(Now())>6,Year(Now())+1,Year(Now())) AS FY
FROM table;
Вот запрос, который действительно работает при доступе. Я это тестировал. Он использует подзапрос как "двойной": (SELECT count(*) as cnt from MSysObjects).
select 1 as a from (SELECT count(*) as cnt from MSysObjects) UNION ALL
select 2 as a from (SELECT count(*) as cnt from MSysObjects) UNION ALL
select 3 as a from (SELECT count(*) as cnt from MSysObjects)