I'm trying try to insert rows from my select statement. But I get a syntax error at the first FROM in the statement. What am I doing wrong?
INSERT INTO [dbo].[OrganizationControlGroup]
VALUES
(
OrganizationId,
9999,
NULL,
CONVERT(DATE,SYSDATETIME()),
NULL,
CONVERT(DATE,SYSDATETIME())
)
FROM
(
SELECT TOP 450 o.OrganizationId
FROM Organization o
WHERE NOT EXISTS
(
SELECT *
FROM OrganizationControlGroup c
WHERE c.OrganizationId = o.OrganizationId
)
)
Your syntax is wrong. You can use INSERT either with VALUES and one (or in newer SQL Server versions more) single record expression list enclosed in parentheses, or with SELECT. If you use the SELECT variant, note that some column expressions can be constants or expressions like your CONVERT(DATE,SYSDATETIME()) that do not depend on the source table. The correct version of your statement would be:
INSERT INTO [dbo].[OrganizationControlGroup]
SELECT TOP 450
o.OrganizationId,
9999,
NULL,
CONVERT(DATE,SYSDATETIME()),
NULL,
CONVERT(DATE,SYSDATETIME())
FROM Organization o
WHERE NOT EXISTS
(
SELECT *
FROM OrganizationControlGroup c
WHERE c.OrganizationId = o.OrganizationId
)
The documentation for INSERT is here: http://msdn.microsoft.com/en-us/library/ms174335.aspx
Related
Snowflake is throwing an error for an EXISTS clause if a filter condition depends on coalescing columns from both the outer table and the subquery table. The query will run if I remove the outer-table column from the COALESCE or replace the COALESCE with the long-form equivalent logic.
I'm seeing this error, specifically SQL compilation error: Unsupported subquery type cannot be evaluated, for what I would consider to be a fairly straightforward WHERE EXISTS clause. This would work in every (recent) SQL variant that I've used (e.g., SQL Server, Postgres), so I'm a little concerned that Snowflake doesn't support it. Am I missing something?
I found what seems to be a similar question at Snowflake's Community in 2019, where Snowflake was failing when the EXISTS clause included a WHERE filter condition that referenced a column from an outer query for something other than joining the tables. There was not a clear solution there.
Snowflake's documentation on its limited support for subqueries says that it supports both correlated and uncorrelated subqueries for "EXISTS, ANY / ALL, and IN subqueries in WHERE clauses".
So why is it failing on this EXISTS clause? Is what I'm seeing a bug, or is this a Snowflake limitation that is not clearly documented?
Code to reproduce the issue:
CREATE OR REPLACE TEMPORARY TABLE Employee (
Emp_SK INT NOT NULL
);
CREATE OR REPLACE TEMPORARY TABLE Employee_X_Pay_Rate (
Emp_SK INT NOT NULL, Pay_Rate_SK INT NOT NULL, Start_Date TIMESTAMP_NTZ NOT NULL, End_Date TIMESTAMP_NTZ NOT NULL
);
CREATE OR REPLACE TEMPORARY TABLE Employee_X_Location (
Emp_SK INT NOT NULL, Location_SK INT NOT NULL, Start_Date TIMESTAMP_NTZ NOT NULL, End_Date TIMESTAMP_NTZ NULL
);
INSERT INTO Employee
VALUES (1);
INSERT INTO Employee_X_Pay_Rate
VALUES
(1, 1, '2018-01-01', '2019-03-31')
,(1, 2, '2019-04-01', '2021-03-31')
,(1, 3, '2021-04-01', '2099-12-31')
;
INSERT INTO Employee_X_Location
VALUES
(1, 101, '2018-01-01', '2019-12-31')
,(1, 102, '2020-01-01', '2020-12-31')
,(1, 103, '2021-01-01', NULL)
;
SET Asof_Date = TO_DATE('2021-05-31', 'yyyy-mm-dd'); -- changing this to TO_TIMESTAMP makes no difference
SELECT
emp.Emp_SK
,empPay.Pay_Rate_SK
,$Asof_Date AS Report_Date
,empPay.Start_Date AS Pay_Start_Date
,empPay.End_Date AS Pay_End_Date
FROM Employee emp
INNER JOIN Employee_X_Pay_Rate empPay
ON emp.Emp_SK = empPay.Emp_SK
AND $Asof_Date BETWEEN empPay.Start_Date AND empPay.End_Date
WHERE EXISTS (
SELECT 1 FROM Employee_X_Location empLoc
WHERE emp.Emp_SK = empLoc.Emp_SK
-- Issue: Next line fails. empLoc.End_Date can be null
AND $Asof_Date BETWEEN empLoc.Start_Date AND COALESCE(empLoc.End_Date, empPay.End_Date)
);
The query will run if I replace the issue line with either of the following.
-- Workaround 1
AND (
$Asof_Date >= empLoc.Start_Date
AND ($Asof_Date <= empLoc.End_Date OR (empLoc.End_Date IS NULL AND $Asof_Date <= empPay.End_Date))
)
-- Workaround 2
AND $Asof_Date BETWEEN empLoc.Start_Date AND COALESCE(empLoc.End_Date, CURRENT_DATE)
I see this still happens, and I just notice you already know about swapping empPay.End_Date to CURRENT_DATE which is how I would have written it.
It does make the correlated sub query more complex because now you are mixing in two tables instead of one.
when CURRENT_DATE is used the SQL is the same as:
SELECT
s.emp_sk
,ep.pay_rate_sk
,TO_DATE('2021-05-31') AS report_date
,ep.start_date AS pay_start_date
,ep.end_date AS pay_end_date
FROM (
SELECT
e.emp_sk
FROM employee e
WHERE EXISTS (
SELECT 1
FROM employee_x_location AS el
WHERE e.emp_sk = el.emp_sk
AND TO_DATE('2021-05-31') BETWEEN el.start_date AND COALESCE(el.end_date, CURRENT_DATE)
)
) AS s
JOIN employee_x_pay_rate AS ep
ON s.emp_sk = ep.emp_sk
AND TO_DATE('2021-05-31') BETWEEN ep.start_date AND ep.end_date;
so demonstrating the correlation is complex verse simple can be shown, by swapping employee table with employee_x_pay_rate in the sub-select, like so:
SELECT
e.emp_sk
FROM Employee_X_Pay_Rate e
WHERE EXISTS (
SELECT 1
FROM employee_x_location AS el
WHERE e.emp_sk = el.emp_sk
AND TO_DATE('2021-05-31') BETWEEN el.start_date AND COALESCE(el.end_date, CURRENT_DATE)
)
works, but use the value from that table does not:
SELECT
e.emp_sk
FROM Employee_X_Pay_Rate e
WHERE EXISTS (
SELECT 1
FROM employee_x_location AS el
WHERE e.emp_sk = el.emp_sk
AND TO_DATE('2021-05-31') BETWEEN el.start_date AND COALESCE(el.end_date, e.End_Date)
)
sign IFNULL(el.end_date, e.End_Date) and NVL(el.end_date, e.End_Date) both fail also.
But you can restruct the code to move the COALESCE into a CTE, and then use the WHERE EXISTS like so:
WITH r_emp_pay AS (
SELECT
empPay.Emp_SK
,empPay.Pay_Rate_SK
,empPay.Start_Date
,empPay.End_Date
FROM Employee_X_Pay_Rate AS empPay
WHERE TO_DATE('2021-05-31', 'yyyy-mm-dd') BETWEEN empPay.Start_Date AND empPay.End_Date
), r_emp_loc AS (
SELECT
empLoc.Emp_SK
,empLoc.Start_Date
,empLoc.End_Date
,COALESCE(empLoc.End_Date, empPay.End_Date) as col_end_date
FROM Employee_X_Location empLoc
JOIN r_emp_pay empPay
ON empPay.Emp_SK = empLoc.Emp_SK
WHERE TO_DATE('2021-05-31', 'yyyy-mm-dd') BETWEEN empLoc.Start_Date AND COALESCE(empLoc.End_Date, CURRENT_DATE)
)
SELECT
emp.Emp_SK
,empPay.Pay_Rate_SK
,TO_DATE('2021-05-31', 'yyyy-mm-dd') AS Report_Date
,empPay.Start_Date AS Pay_Start_Date
,empPay.End_Date AS Pay_End_Date
FROM Employee emp
JOIN r_emp_pay empPay
ON emp.Emp_SK = empPay.Emp_SK
WHERE EXISTS (
SELECT 1 FROM r_emp_loc empLoc
WHERE emp.Emp_SK = empLoc.Emp_SK
AND TO_DATE('2021-05-31', 'yyyy-mm-dd') BETWEEN empLoc.Start_Date AND empLoc.col_end_date
);
gives:
EMP_SK
PAY_RATE_SK
REPORT_DATE
PAY_START_DATE
PAY_END_DATE
1
3
2021-05-31
2021-04-01 00:00:00.000
2099-12-31 00:00:00.000
I have been looking into this and it looks like Snowflake supports correlated subquery in where clause if it is convinced that the inner subquery returns a scalar result. So in short, using functions like COUNT, ANY_VALUE or DISTINCT can give the result.
Consider the following query -
SELECT * FROM Department D
INNER JOIN (select * from Employee) E
ON D.DepartmentID = E.DepartmentID
where EXISTS( SELECT **distinct** 1 FROM EMPLOYEES E1 WHERE E1.DEPARTMENTID=D.DEPARTMENTID );
(ignore the similar tables, they were used for testing).
My requirement was to have TOP 1 in the WHERE EXISTS clause. However, since Snowflake does not support it, I managed the same using DISTINCT.
That being said, your query might work with the same WHERE EXISTS(SELECT DISTINCT 1.... as you need just to know whether records exist or not.
References to [Snowflake Community Forum-]:(https://community.snowflake.com/s/question/0D53r00009mIxwYCAS/sql-compilation-error-unsupported-subquery-type-cannot-be-evaluated?t=1626899830543)
(https://community.snowflake.com/s/question/0D50Z00008BDZz0SAH/subquery-in-select-clause)
The question I asked yesterday was simplified but I realize that I have to report the whole story.
I have to extract the data of 4 from 4 different tables into a Firebird 2.5 database and the following query works:
SELECT
PRODUZIONE_T t.CODPRODUZIONE,
PRODUZIONE_T.NUMEROCOMMESSA as numeroco,
ANGCLIENTIFORNITORI.RAGIONESOCIALE1,
PRODUZIONE_T.DATACONSEGNA,
PRODUZIONE_T.REVISIONE,
ANGUTENTI.NOMINATIVO,
ORDINI.T_DATA,
FROM PRODUZIONE_T
LEFT OUTER JOIN ORDINI_T ON PRODUZIONE_T.CODORDINE=ORDINI_T.CODORDINE
INNER JOIN ANGCLIENTIFORNITORI ON ANGCLIENTIFORNITORI.CODCLIFOR=ORDINI_T.CODCLIFOR
LEFT OUTER JOIN ANGUTENTI ON ANGUTENTI.IDUTENTE = PRODUZIONE_T.RESPONSABILEUC
ORDER BY right(numeroco,2) DESC, left(numeroco,3) desc
rows 1 to 500;
However the query returns me double (or more) due to the REVISIONE column.
How do I select only the rows of a single NUMEROCOMMESSA with the maximum REVISIONE value?
This should work:
select COD, ORDER, S.DATE, REVISION
FROM TAB1
JOIN
(
select ORDER, MAX(REVISION) as REVISION
FROM TAB1
Group By ORDER
) m on m.ORDER = TAB1.ORDER and m.REVISION = TAB1.REVISION
Here you go - http://sqlfiddle.com/#!6/ce7cf/4
Sample Data (as u set it in your original question):
create table TAB1 (
cod integer primary key,
n_order varchar(10) not null,
s_date date not null,
revision integer not null );
alter table tab1 add constraint UQ1 unique (n_order,revision);
insert into TAB1 values ( 1, '001/18', '2018-02-01', 0 );
insert into TAB1 values ( 2, '002/18', '2018-01-31', 0 );
insert into TAB1 values ( 3, '002/18', '2018-01-30', 1 );
The query:
select *
from tab1 d
join ( select n_ORDER, MAX(REVISION) as REVISION
FROM TAB1
Group By n_ORDER ) m
on m.n_ORDER = d.n_ORDER and m.REVISION = d.REVISION
Suggestions:
Google and read the classic book: "Understanding SQL" by Martin Gruber
Read Firebird SQL reference: https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25.html
Here is yet one more solution using Windowed Functions introduced in Firebird 3 - http://sqlfiddle.com/#!6/ce7cf/13
I do not have Firebird 3 at hand, so can not actually check if there would not be some sudden incompatibility, do it at home :-D
SELECT * FROM
(
SELECT
TAB1.*,
ROW_NUMBER() OVER (
PARTITION BY n_order
ORDER BY revision DESC
) AS rank
FROM TAB1
) d
WHERE rank = 1
Read documentation
https://community.modeanalytics.com/sql/tutorial/sql-window-functions/
https://www.firebirdsql.org/file/documentation/release_notes/html/en/3_0/rnfb30-dml-windowfuncs.html
Which of the three (including Gordon's one) solution would be faster depends upon specific database - the real data, the existing indexes, the selectivity of indexes.
While window functions can make the join-less query, I am not sure it would be faster on real data, as it maybe can just ignore indexes on order+revision cortege and do the full-scan instead, before rank=1 condition applied. While the first solution would most probably use indexes to get maximums without actually reading every row in the table.
The Firebird-support mailing list suggested a way to break out of the loop, to only use a single query: The trick is using both windows functions and CTE (common table expression): http://sqlfiddle.com/#!18/ce7cf/2
WITH TMP AS (
SELECT
*,
MAX(revision) OVER (
PARTITION BY n_order
) as max_REV
FROM TAB1
)
SELECT * FROM TMP
WHERE revision = max_REV
If you want the max revision number in Firebird:
select t.*
from tab1 t
where t.revision = (select max(t2.revision) from tab1 t2 where t2.order = t.order);
For performance, you want an index on tab1(order, revision). With such an index, performance should be competitive with any other approach.
I'm attempting to do an essentially very simple task, which is resulting in:
ORA-00928: missing SELECT keyword
All I'm trying to do is persist the results from periods into table globalTable. Selecting works fine (I'm returned rows) however as soon as I replace it with the Insert I get the above error.
create global temporary table globalTable
(
ids number(11)
);
with periods as
(
select cl.id uniqueId
from inv_mpan_hh_con_lines cl
left join invoice_vat_lines vl on
cl.invoice_id = VL.INVOICE_ID
where rownum < 4
)
--//Issue occurs at insert keyword. If I comment it and uncomment select it works as expected//--
--select uniqueId
insert into globalTable
from periods;
Any pointers are much appreciated.
Try this:
insert into globalTable
with periods as
(
select cl.id uniqueId
from inv_mpan_hh_con_lines cl
left join invoice_vat_lines vl
on cl.invoice_id = VL.INVOICE_ID
where rownum < 4
)
select uniqueId
from periods;
CTE (WITH-clause) is a part of SELECT statement, according to the INSERT syntax you can either specify values or SELECT statement
I have created a very simple example showing an issue I have encountered on a much more complicated query using dynamic SQL and I can't figure out why the CTE is causing the error when the standard version is not.
Error when using the CTE:
Conversion failed when converting date and/or time from character string.
I am trying to achieve the following with my dynamic query:
Flag records that don't match a specified data type (date in this case). If they pass that step they are later checked against a look-up table to validate they are the values I was expecting (validate dates in a specified range).
-- Table Setup
CREATE TABLE #ValidDate
(
V INT IDENTITY (1, 1) NOT NULL,
VDate DATE NULL
)
INSERT INTO #ValidDate
VALUES ('02/20/2014'), ('02/21/2014'), ('02/22/2014'), ('02/23/2014'), ('02/25/2014')
CREATE TABLE #DatesToValidate
(
I INT IDENTITY (1, 1) NOT NULL,
IDate VARCHAR(30) NULL
)
INSERT INTO #DatesToValidate
VALUES ('apple'), ('02/21/2014'), ('orange'), ('02/23/2014')
CREATE TABLE #Errors
(
ID INT,
Dates VARCHAR(30)
)
INSERT INTO #Errors
SELECT *
FROM #DatesToValidate
WHERE ISDATE(IDate) <> 1
Below is the standard Query (Works):
SELECT *
FROM #DatesToValidate
WHERE I NOT IN ( SELECT ID
FROM #Errors)
AND IDate NOT IN ( SELECT VDate
FROM #ValidDate)
Below is the CTE (Does not Work):
;WITH CTE AS
(
SELECT *
FROM #DatesToValidate
WHERE I NOT IN ( SELECT ID
FROM #Errors)
)
SELECT *
FROM CTE
WHERE IDate NOT IN ( SELECT VDate
FROM #ValidDate)
This SQL Fiddle shows the problem. Neither version is guaranteed to work. If you swap the order of the conditions in the and then you'll get a conversion failure.
The reason the and version happens to work in this case is because of short-circuiting. When the first condition fails, the second doesn't get run. This is an optimization that SQL Server can take advantage of. And does in this case, so you think it works.
The CTE version doesn't work because there is no guarantee on the ordering of the operations. And SQL Server decides to do something different. Namely, it tries to evaluate 'apple' as a date.
The right solution for both is to cast before the compare. Because the cast may not work, you need to be careful. You should use case in SQL Server for this purpose; it is the only construct that guarantees sequential evaluation.
So, try this:
;WITH CTE AS
(
SELECT *
FROM DatesToValidate
WHERE I NOT IN ( SELECT ID
FROM Errors)
)
SELECT *
FROM CTE
WHERE (case when isdate(IDate) = 1 then cast(IDate as date)
end) NOT IN ( SELECT VDate
FROM ValidDate)
This particular part is failing cause VDate is a date type column and IDate is varchar. So you need to cast VDate like cast(VDate as varchar(30) as shown below.
See a demo fiddle here http://sqlfiddle.com/#!3/abfc0/7
WHERE IDate NOT IN (SELECT VDate FROM ValidDate) <--- Failing
Try your sql this way
;WITH CTE AS
(
SELECT *
FROM DatesToValidate
WHERE I NOT IN ( SELECT ID
FROM Errors)
)
SELECT *
FROM CTE
WHERE IDate NOT IN (SELECT cast(VDate as varchar(30)) <--- Cast Here
FROM ValidDate)
I'm sure this has been asked somewhere, but I found it difficult to search for.
If I want to get all records where a column value equals one in a list, I'd use the IN operator.
SELECT idSparePart, SparePartName
FROM tabSparePart
WHERE SparePartName IN (
'1234-2043','1237-8026','1238-1036','1238-1039','1223-5172'
)
Suppose this SELECT returns 4 rows although the list has 5 items. How can I select the value that does not occur in the table?
Thanks in advance.
select t.* from (
select '1234-2043' as sparePartName
union select '1237-8026'
union select '1238-1036'
union select '1238-1039'
union select '1223-5172'
) t
where not exists (
select 1 from tabSparePart p WHERE p.SparePartName = t.sparePartName
)
As soon as you mentioned that i have to create a temp table, i remembered my Split-function.
Sorry for answering my own question, but this might be the the best/simplest way for me:
SELECT PartNames.Item
FROM dbo.Split('1234-2043,1237-8026,1238-1036,1238-1039,1223-5172', ',') AS PartNames
LEFT JOIN tabSparePart ON tabSparePart.SparePartName = PartNames.Item
WHERE idSparePart IS NULL
My Split-function:
Help with a sql search query using a comma delimitted parameter
Thank you all anyway.
Update: I misunderstood the question. I guess in that case I would select the values into a temp table, then select the values which are not in that table. Not ideal, I know -- the problem is that you need to get your list of part names to SQL Server somehow (either via IN or putting them in a temp table) but the semantics of IN don't do what you want.
Something like this:
CREATE TABLE tabSparePart
(
SparePartName nvarchar(50)
)
insert into tabSparePart values('1234-2043')
CREATE TABLE #tempSparePartName
(
SparePartName nvarchar(50)
)
insert into #tempSparePartName values('1234-2043')
insert into #tempSparePartName values('1238-1036')
insert into #tempSparePartName values('1237-8026')
select * from #tempSparePartName
where SparePartName not in (select SparePartName from tabSparePart)
With output:
SparePartName
1238-1036
1237-8026
Original (wrong) answer:
You can just use "not in":
SELECT * from tabSparePart WHERE SparePartName NOT in(
'1234-2043','1237-8026','1238-1036','1238-1039','1223-5172'
)
You could try something like this....
declare #test as table
(
items varchar(50)
)
insert into #test
values('1234-2043')
insert into #test
values('1234-2043')
insert into #test
values('1237-8026')
-- the rest of the values --
select * from #test
where items not in (
select theItemId from SparePartName
)
for fun check this out...
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx
It shows you how to take delimited data and return it from a table valued function as separate "rows"... which my make the process of creating the table to select from easier than inserting into a #table or doing a giant select union subquery.