Why does DELETE FROM ... FROM ... not error out? - sql

This bit within a stored proc is apparently valid sql:
DELETE TOP (#MaxRecords)
FROM Table
FROM Table B
INNER JOIN Table2 R ON B.fk = R.pk
WHERE R.Value < #DecVariable;
How can two FROM statements be put together and yet still be valid?

First of all TOP in delete syntax indicates that it is SQL Server.
It is perfect valid query, see DELETE:
FROM
An optional keyword that can be used between the DELETE keyword and the target table_or_view_name, or rowset_function_limited.
FROM table_source
Specifies an additional FROM clause. This Transact-SQL extension to DELETE allows specifying data from and deleting the
corresponding rows from the table in the first FROM clause.
This extension, specifying a join, can be used instead of a subquery in the WHERE clause to identify rows to be removed.
DELETE:
Object:

Related

MS Access SQL Deleting record based on external table

I had a similar question regarding selecting records based on an external table (in an unlinked table):
Access SQL FROM IN and JOINS
This time I'm trying to delete records in an internal table based on criteria in an external one.
Private Sub Command0_Click()
Dim SQLstring As String
SQLstring = "Delete * FROM Testtable1 AS tnf WHERE ([Network Location].[Testtable2].[Customers] = tnf.[Customers]) AND ([Network Location].[Testtable2].[Contract] = '2003') Or ([Network Location].[Testtable2].[Active]='N');"
DoCmd.RunSQL (SQLstring)
End Sub
Its telling me that " is not a valid name and to check if theres invalid characters or punctuation or if its too long. I cant find anything that talks about SQL lengths but i'm pretty sure this isn't too long.
Is there something I'm missing about my SQl statement I've gone through it over and over and I'm not seeing an issue.
Should the WHERE clause have an Exists (Select) clause?
Am I missing something?
Since a delete query can only delete records from one table at a time, only one dataset can be referenced in the from clause. With your current query, you would need to reference testtable2 within the from clause, which would result in an invalid delete query.
As such, you'll instead need to use where exists with a subquery, e.g.:
delete from testtable1 as t1
where exists
(
select 1 from [Network Location].[Testtable2] t2
where t2.customers = t1.customers and (t2.contract = '2003' or t2.active = 'N')
)
I've also surrounded the or expression with parentheses, as I assume that it is the desired logic to delete records with a matching customers record and either of the other two conditions, rather than deleting any record for which t2.active = 'N' (since and has operator precedence over or).

Return deleted rows in sqlite

It's not efficient to do two queries like SELECT * FROM TABLE WHERE clause and then DELETE * FROM TABLE WHERE clause.
So I want to make DELETE query and return deleted rows (one query).
I tried to do:
DELETE OUTPUT DELETED.*
FROM table
WHERE clause
But I have an error:
SQLite exception: near "OUTPUT": syntax error
How to make it correctly or maybe there is another alternative way to return deleted rows?
The DELETE statement has no OUTPUT clause.
After doing the SELECT, all the important data is in the cache, so the DELETE will run quickly.
And because SELECT plus DELETE is the only way, it is the most efficient way.
Since version 3.35, SQLite has the RETURNING clause.
You can return the records deleted with the returning clause
delete from myTable returning *
You can use the changes() call right after your DELETE query: https://www.sqlite.org/c3ref/changes.html
Implementation will vary depending on the language you're using. For example in PHP you could write:
$sqlite = new SQLite3('database.sqlite');
$statement = $sqlite->prepare('DELETE FROM mytable WHERE myclause');
$statement->execute();
echo $sqlite->changes();

SQL server: difference between DELETE <Table> and DELETE from <Table>

SQL server:
Difference between :
Delete tblxyz where id=6
and :
Delete from tblxyz where id=6
Is their any difference between above queries ?
There is no difference if you see the execution plan both generates delete scripts as below
DELETE [testtable] WHERE [numbers]=#1
There is no direct difference between the two statements
(except that I find the "DELETE FROM" easier to read and understand)
note that ANSI does require the "FROM" keyword as stated by jarlh
see also
https://msdn.microsoft.com/en-us/library/ms189835.aspx
There is no difference in your delete.
However, the FROM clause is a fully functional from clause, so you can use it with JOIN. For instance:
delete t
from t join
(select min(id) as minid
from t
group by grp
) tt
on t.id = tt.minid;
This would delete the record with the minimum id for each grp value. The alias after the delete is needed to specify which table to delete from (although in this case, deleting from an aggregation result is not allowed).
Note: This query is for illustrative purposes. There is nothing wrong with the query, but it is not how I would actually write such a query in SQL Server.
According to the MSDN the word "from" is optional. The default is not to use:
FROM
and the target table_or_view_name, or rowset_function_limited.An optional keyword that can be used between the DELETE keyword
However in MS Access (or may be some other databases) you may delete a row using delete * command, so you have to use from:
delete * from phoneBook where....
The from Clause is optional..Below is stripped down version of Syntax..
DELETE
[ TOP ( expression ) [ PERCENT ] ]
[ FROM ]
{ { table_alias
FROM
An optional keyword that can be used between the DELETE keyword and the target table_or_view_name, or rowset_function_limited.

Why do I need the 'match' part of a SQL merge, in this scenario?

Consider the following:
merge into T t1
using (select ID,Col1 from T where ID = 123) t2
on 1 = 0
when not matched then insert (Col1) values (t2.Col1);
Cominig from a programming background, to me this translates to:
"Evaluate false (i.e. 1 = 0), and when it is false (i.e. all the time), insert."
Is it not possible to just omit the match condition?
Is it because of my select's where condition that I'm confused here? Should this condition be moved to the on?
NOTE:
Due to restrictions with output, I cannot use insert. I need to output the results of this merge into a temporary table for reasons outside of the scope of what I'm asking.
In the answer you've linked to in the comments, as I've hopefully made clear, we are abusing the MERGE statement.
The query you've shown here could trivially be replaced by:
insert into T(Col1) select Col1 from T where ID = 123
However, if you want to be able to add an OUTPUT clause, and that OUTPUT clause needs to reference both the newly inserted data and data from the source table, you're not allowed to write such a clause on an INSERT statement.
So, we instead use a MERGE statement, but not for its intended purpose. The entire purpose is to force it to perform an INSERT and write our OUTPUT clause.
If we examine the documentation for MERGE, we see that the only clause in which we can specify to perform an INSERT is in the WHEN NOT MATCHED [BY TARGET] clause - in both the WHEN MATCHED and WHEN NOT MATCHED BY SOURCE clauses, our only options are to UPDATE or DELETE.
So, we have to write the MERGE such that matching always fails - and the simplest way to do that is to say that matching should occur when 1 = 01 - which, hopefully, is never.
1Since SQL Server doesn't support boolean literals

Update from aggregate in same table if aggregate value wrong - SQL Server/Oracle/Firebird

I have a table with grouped tasks:
tt_plan_task_id is the id
records with tt_plantype=1 represent 'groups'
tasks in/under a group have a tt_group_id pointing to the tt_plan_task_id
there are tasks that don't belong to a group (tt_group_id is null)
groups nest multiple levels
I need to fix (update) the tt_fromdate field values for the group records if they do not match the min(tt_fromdate) from the underlying tasks (they always have a value).
To fix them all I could do
update tt_plan_task g
set tt_fromdate=
(select min(t.tt_fromdate) from tt_plan_task t
where (t.tt_group_id=g.tt_plan_task_id))
where (g.tt_plantype=1)
This statement avoids the UPDATE FROM syntax that I see in many (SQL server) answers - Firebird does not support that.
There are 2 complications
I want to do the update only if g.tt_fromdate <> min(t.tt_fromdate), so I would have to add a reference to min(tt_fromdate) to the outer where.
I tried using an alias for the aggregate and referencing that but that got me nowhere (syntax errors)
SQL Server does not like the table alias in the update, but solutions like these use the UPDATE FROM syntax again ;-( How do I work around that then?
How do I tie 1. and 2. into my update statement so that it works?
As noted in the title, this needs to execute in SQL Server, Oracle, and Firebird
Note: Since groups can contain groups, the update should ideally be executed 'from the bottom up', i.e. deepest groups first.
But since this is just a rough correction for a corrupt database, doing one 'lineair' pass over all groups is good enough.
To get around SQL Server's non-standard way for update table aliases, simply don't use any.
As to using the aggregate result in both the SET clause and the WHERE clause, I suppose the only way all DBMS work with, is to write the aggregation query twice.
update tt_plan_task
set tt_fromdate =
(
select min(t.tt_fromdate)
from tt_plan_task t
where t.tt_group_id = tt_plan_task.tt_plan_task_id
)
where (tt_plantype=1)
and
(
tt_fromdate <>
(
select min(t.tt_fromdate)
from tt_plan_task t
where t.tt_group_id = tt_plan_task.tt_plan_task_id
)
);