"Update if table exists" in one statement - how? - sql

I have a problem with SQL statement which is a part of long transaction.
UPDATE tab_1
LEFT JOIN tab_2
ON tab_1.id = tab_2.tab_1_id
SET tab_1.something = 42
WHERE tab1.id = tab_2.tab_1_id;
Everything is simple and works fine as long as tab_1 and tab_2 exist in database, which is obvious. ;-)
The problem is that the transaction have to be commited on 4 different servers, and tab_2 is "dynamic" table, which may exist in specific db / db schema, or not...
If tab_2 don't exists, database threw exception and the whole transaction is not commited. What I want is to continue anyway (just update 0 rows)!
I have tried something like this:
UPDATE [all the same as above] WHERE tab1.id = tab_2.tab_1_id AND EXISTS (select 1 from pg_class where relname='tab_2');
...but it's still wrong, since "exception-check" is made before "where" condition (it's the same table we want to use in join..).
Is there any way to do this with "pure" SQL? :)
Something like: LEFT JOIN tab_2 IF tab_2 EXISTS (and if not - do nothing, return null, etc.?)
I know there is a way to do this in pl/pgsql procedure.
The second possibility is to create table if not exists, before the statement..
But maybe there is some kind of simple and elegant way to do this in one statement? :)
DBMS: PostgreSQL 9.2

I don't think an UPDATE statement that succeeds even if a table doesn't exist is simple and elegant. I think it is strange and confusing.
Why not just include a conditional that checks whether that table exists, and only perform the update if it exists? It would be a lot clearer.
Another option would be to create a view that points to tab_2 if it exists, or points to an empty table otherwise. This could be helpful if you have a lot of queries like this, and you don't want to change them all.
Update: Here is what a condition would look like (has to be within a function or a BEGIN...END block):
IF EXISTS (select 1 from pg_class where relname='tab_2') THEN
UPDATE...
END IF;
Depending on the details of Postgresql, this still might fail compilation if it sees a table that doesn't exist in the UPDATE statement (I'm not a Postgresql user). In that case, you would need to create a view that points to tab_2 if it exists, and an empty table if it doesn't exist..

Related

Informix and DROP COLUMN if exists

I am trying to write "re-usable" SQL script with ADD COLUMN clause for Informix server. Normally, I can only run it once, because then the column already exists and it cannot be added again. And I cannot just add DROP column as prior statement, because it would fail during the first execution. I need something like DROP COLUMN IF EXISTS, but unlike dropping other SQL entities, it is not available for columns.
I can ask system tables whether the column is present in then DB via:
SELECT c.colname
FROM "informix".systables AS t
JOIN "informix".syscolumns AS c ON t.tabid = c.tabid
WHERE c.colname = 'col_name' and t.tabname = 'tab_name'
plus I found some proposed solutions for workarounds, but I guess they are for different SQL servers, since I cannot figure out how to put it together into valid SQL script acceptable for my Informix.
Any clues? Or is it mission impossible?
As things stand, Informix's ALTER TABLE statement has no DROP COLUMN IF EXISTS clause, nor an ADD COLUMN IF NOT EXISTS clause (nor a separate DROP COLUMN statement — though it does have a separate RENAME COLUMN statement).
You would have to wrap the logic to detect whether the column exists in a shell script and decide based on that whether to add the column. Or, alternatively, you'd have to submit the ADD operation and ignore the 'column already exists' error.

SQLite Script IF NOT EXISTS (...) - Alternatives

I'm looking for an alternative to the IF-ELSE-Statement as known in MS-SQL for SQLite.
Try searching on stackoverflow first!
I did. I found something like that:
SQLite 'IF NOT EXISTS' syntax error
SQLite if statement in WHERE clause
and so on...
The problem in that cases is that they perform SELECT/INSERT/UPDATE/DELETE.
I want to alter the schema of an existing database.
Creating a new table is trivial because there is CREATE TABLE IF NOT EXISTS "foo" (....);.
But how about adding columns to an existing table?
I want to write a script like:
IF NOT EXISTS (SELECT * FROM pragma_table_info("<table_name>") WHERE name == "<column_name>")
BEGIN
ALTER TABLE "<table_name>" ADD "<column_name>" TEXT;
END
This sounds like ALTER TABLE ADD COLUMN IF NOT EXISTS in SQLite. But this post is out of the year 2010 and I would expect that something has changed in the last 9 years.
I need to do some statements like that using arbitrary queries and statements.
Is there any way to do that in pure SQL or do I have to handle that in application code?

Using table variables in Oracle Stored Procedure

I have lots of experience with T-SQL (MS SQL Server).
There it is quite common to first select some set of records into a
table variable or say temp table t, and then work with this t
throughout the whole SP body using it just like a regular table
(for JOINS, sub-queries, etc.).
Now I am trying the same thing in Oracle but it's a pain.
I get errors all the way and it keeps saying
that it does not recognize my table (i.e. my table variable).
Error(28,7): PL/SQL: SQL Statement ignored
Error(30,28): PL/SQL: ORA-00942: table or view does not exist
I start thinking what at all is possible to do with this
table variable and what not (in the SP body) ?
I have this declaration:
TYPE V_CAMPAIGN_TYPE IS TABLE OF V_CAMPAIGN%ROWTYPE;
tc V_CAMPAIGN_TYPE;
What on Earth can I do with this tc now in my SP?!
This is what I am trying to do in the body of the SP.
UPDATE ( SELECT t1.STATUS_ID, t2.CAMPAIGN_ID
FROM V_CAMPAIGN t1
INNER JOIN tc t2 ON t1.CAMPAIGN_ID = t2.CAMPAIGN_ID
) z
SET z.STATUS_ID = 4;
V_CAMPAIGN is a DB view, tc is my table variable
Presumably you are trying to update a subset of the V_CAMPAIGN records.
While in SQLServer it may be useful to define a 'temporary' table containing the subset and then operate on that it isn't necessary in Oracle.
Simply update the table with the where clause you would have used to define the temp table.
E.g.
UPDATE v_campaign z
SET z.status_id = 4
WHERE z.column_name = 'a value'
AND z.status <> 4
I assume that the technique you are familiar with is to minimise the effect of read locks that are taken while selecting the data.
Oracle uses a different locking strategy so the technique is mostly unnecessary.
Echoing a comment above - tell us what you want to achieve in Oracle and you will get suggestions for the best way forward.

informix check if table exists and then read the value

I have a table in informix (Version 11.50.UC4) called NextRecordID with just one column called id and it will have one row. What I want to do is copy this value into another table. But don't want my query to fail if this table does not exist. Something like
if table NextRecordID exists
then insert into sometable values ('NextRecordID', (select id from NextRecordID))
else insert into sometable values ('NextRecordID', 1)
I ended up using the below SQL query. Its not ANSI SQL but works the informix server I am using.
insert into sometable values ('NextRecordID',
select case (select 1 from systables where tabname='nextrecordid')
when 1 then (select nextid from nextrecordid)
else (select 1 from systables where tabname='systables') end
from systables where tabname='systables');
What is happening here is within insert query I get the value to be inserted by using select query. Now that select query is interesting. It uses case statement of Informix. I have written a select query to check if the table nextrecordid exists in systables and return 1 if it exists. If this query returns 1, I query the table nextrecordid for the value or else I wrote a query to return the default value 1. This work for me.
You should be able to do this by checking the systables table.
Thank you for including server version information - it makes answering your question easier.
You've not indicated which language(s) you are using.
Normally, though, you design a program to expect a certain schema (certain tables to be present), and then fail - preferably under control - if those tables are not present. Also, it is not clear whether you would get into problems because of repeated execution of the second INSERT statement. Nor is it clear when the NextRecordID table is updated - presumably, once the value has been used, it must be updated.
You should look at SERIAL (BIGSERIAL) and see whether that is appropriate for you.
You should also look at whether a SEQUENCE would be appropriate to use here - it certainly looks rather like it might be applicable.
As Adam Hughes points out, if you want to check whether the NextRecordID table is present in the database, you would look in the systables table. Be aware, though, that your search will need to be against an all lower-case name (nextrecordid).
Also, MODE ANSI databases complicate life - you have to worry about the table's owner (because there could be multiple tables called nextrecordid in a MODE ANSI database). Most likely, you don't have to worry about that - any more than you are likely to have to worry about delimited identifiers for table "someone"."NextRecordID" (which is a different table from someone.NextRecordID).

Operation must use an updatable query. (Error 3073)

I have written this query:
UPDATE tbl_stock1 SET
tbl_stock1.weight1 = (
select (b.weight1 - c.weight_in_gram) as temp
from
tbl_stock1 as b,
tbl_sales_item as c
where
b.item_submodel_id = c.item_submodel_id
and b.item_submodel_id = tbl_stock1.item_submodel_id
and b.status <> 'D'
and c.status <> 'D'
),
tbl_stock1.qty1 = (
select (b.qty1 - c.qty) as temp1
from
tbl_stock1 as b,
tbl_sales_item as c
where
b.item_submodel_id = c.item_submodel_id
and b.item_submodel_id = tbl_stock1.item_submodel_id
and b.status <> 'D'
and c.status <> 'D'
)
WHERE
tbl_stock1.item_submodel_id = 'ISUBM/1'
and tbl_stock1.status <> 'D';
I got this error message:
Operation must use an updatable query. (Error 3073) Microsoft Access
But if I run the same query in SQL Server it will be executed.
Thanks,
dinesh
I'm quite sure the JET DB Engine treats any query with a subquery as non-updateable. This is most likely the reason for the error and, thus, you'll need to rework the logic and avoid the subqueries.
As a test, you might also try to remove the calculation (the subtraction) being performed in each of the two subqueries. This calculation may not be playing nicely with the update as well.
Consider this very simple UPDATE statement using Northwind:
UPDATE Categories
SET Description = (
SELECT DISTINCT 'Anything'
FROM Employees
);
It fails with the error 'Operation must use an updateable query'.
The Access database engine simple does not support the SQL-92 syntax using a scalar subquery in the SET clause.
The Access database engine has its own proprietary UPDATE..JOIN..SET syntax but is unsafe because, unlike a scalar subquery, it doesn’t require values to be unambiguous. If values are ambiguous then the engine silent 'picks' one arbitrarily and it is hard (if not impossible) to predict which one will be applied even if you were aware of the problem.
For example, consider the existing Categories table in Northwind and the following daft (non-)table as a target for an update (daft but simple to demonstrate the problem clearly):
CREATE TABLE BadCategories
(
CategoryID INTEGER NOT NULL,
CategoryName NVARCHAR(15) NOT NULL
)
;
INSERT INTO BadCategories (CategoryID, CategoryName)
VALUES (1, 'This one...?')
;
INSERT INTO BadCategories (CategoryID, CategoryName)
VALUES (1, '...or this one?')
;
Now for the UPDATE:
UPDATE Categories
INNER JOIN (
SELECT T1.CategoryID, T1.CategoryName
FROM Categories AS T1
UNION ALL
SELECT 9 - T2.CategoryID, T2.CategoryName
FROM Categories AS T2
) AS DT1
ON DT1.CategoryID = Categories.CategoryID
SET Categories.CategoryName = DT1.CategoryName;
When I run this I'm told that two rows have been updated, funny because there's only one matching row in the Categories table. The result is that the Categories table with CategoryID now has the '...or this one?' value. I suspect it has been a race to see which value gets written to the table last.
The SQL-92 scalar subquery is verbose when there are multiple clauses in the SET and/or the WHERE clause matches the SET's clauses but at least it eliminates ambiguity (plus a decent optimizer should be able to detects that the subqueries are close matches). The SQL-99 Standard introduced MERGE which can be used to eliminate the aforementioned repetition but needless to say Access doesn't support that either.
The Access database engine's lack of support for the SQL-92 scalar subquery syntax is for me its worst 'design feature' (read 'bug').
Also note the Access database engine's proprietary UPDATE..JOIN..SET syntax cannot anyhow be used with set functions ('totals queries' in Access-speak). See Update Query Based on Totals Query Fails.
Keep in mind that if you copy over a query that originally had queries or summary queries as part of the query, even though you delete those queries and only have linked tables, the query will (mistakenly) act like it still has non-updateable fields and will give you this error. You just simply re-create the query as you want it but it is an insidious little glitch.
You are updating weight1 and qty1 with values that are in turn derived from weight1 and qty1 (respectively). That's why MS-Access is choking on the update. It's probably also doing some optimisation in the background.
The way I would get around this is to dump the calculations into a temporary table, and then update the first table from the temporary table.
There is no error in the code. But the error is Thrown because of the following reason.
Please check weather you have given Read-write permission to MS-Access database file.
The Database file where it is stored (say in Folder1) is read-only..?
suppose you are stored the database (MS-Access file) in read only folder, while running your application the connection is not force-fully opened. Hence change the file permission / its containing folder permission like in C:\Program files all most all c drive files been set read-only so changing this permission solves this Problem.
In the query properties, try changing the Recordset Type to Dynaset (Inconsistent Updates)