I'm trying to delete rows in an SQLite database in an Android application.
Here is the query and the Java code calling it:
final Cursor cursor = mDb.rawQuery("DELETE FROM link WHERE version!=? AND sentence IN (SELECT _id FROM sentence WHERE language=?) AND translation IN (SELECT _id FROM sentence WHERE language=?)", new String[]{String.valueOf(versionToConserve), sentenceLanguage, translationLanguage});
cursor.moveToFirst();
cursor.close();
The application freezes on cursor.moveToFirst().
Here is the EXPLAIN QUERY PLAN:
selectid:0, order:0, from:0, detail:SEARCH TABLE link USING INDEX sqlite_autoindex_link_1 (sentence=? AND translation=?),
selectid:0, order:0, from:0, detail:EXECUTE LIST SUBQUERY 0,
selectid:0, order:0, from:0, detail:SCAN TABLE sentence,
selectid:0, order:0, from:0, detail:EXECUTE LIST SUBQUERY 1,
selectid:1, order:0, from:0, detail:SCAN TABLE sentence,
I thought that maybe the query could be too slow, but after half an hour, it is still stucked here.
I tried to replace DELETE by SELECT, it freezes as well.
I tried the inner SELECT queries alone, they work perfectly.
I tried to replace the inner SELECT queries by (1,2,3) and (4,5,6), it works.
I tried to use JOIN instead of IN (SELECT ...) but it doesn't accept it. It says LIMIT, WHERE or other terms are expected instead of JOIN.
I don't know how to investigate more. Any ideas?
Don't use rawQuery() to delete rows.
The method rawQuery() is used to return rows with a SELECT statement, in the form of a Cursor.
Use delete():
String strWhere = "version <> ? AND " +
"sentence IN (SELECT _id FROM sentence WHERE language = ?) AND " +
"translation IN (SELECT _id FROM sentence WHERE language = ?)";
int rows = mDB.delete(
"link",
strWhere,
new String[]{String.valueOf(versionToConserve), sentenceLanguage, translationLanguage}
);
The returned value of delete() which is assigned to the variable rows contains the number of deleted rows.
I finally got it to work with this WHERE clause:
version<>?
AND EXISTS (SELECT 1 FROM sentence WHERE language=? AND sentence=_id)
AND EXISTS (SELECT 1 FROM sentence WHERE language=? AND translation=_id)
I have a query which I am trying to test. The query should update the data if it finds data in the table with existing primary key. If it doesn't then insert into the table.
The Primary key is of type int and in the properties I can see Identity is set to "True" which I assume it means that it will automatically set the new id for the primary if it is inserted.
MERGE INTO Test_table t
USING (SELECT 461232 ID,'Test1-data' Fascia FROM Test_table) s
ON (t.ID = s.ID)
WHEN MATCHED THEN
UPDATE SET t.Fascia = s.Fascia
WHEN NOT MATCHED THEN
INSERT (Fascia)
VALUES (s.Fascia);
The issue here is this query doesn't work and it never inserts the data or updates. Also, query gets compiled and I don't get any compilation error
Also the reason I want this query is to work because then I will use Java prepared statement to query the database so I am assuming I can do
SELECT ? ID,? Fascia FROM Test_table
So that I can pass the values with set methods in java.
Please let me know if there is something wrong in my query.
You are selecting from the target table as your source.
You either need to remove your FROM Test_table or have at least 1 row in Test_table prior to your merge.
rextester demo: http://rextester.com/XROJD28508
MERGE INTO Test_table t
USING (SELECT 461232 ID,'Test1-data' Fascia --FROM Test_table
) s
ON (t.ID = s.ID)
WHEN MATCHED THEN
UPDATE SET t.Fascia = s.Fascia
WHEN NOT MATCHED THEN
INSERT (Fascia)
VALUES (s.Fascia);
The UPSERT operation either updates or inserts a row in a table, depending if the table already has a row that matches the data:
if table t has a row exists that has key X:
update t set mystuff... where mykey=X
else
insert into t mystuff...
Since Oracle doesn't have a specific UPSERT statement, what's the best way to do this?
The MERGE statement merges data between two tables. Using DUAL
allows us to use this command. Note that this is not protected against concurrent access.
create or replace
procedure ups(xa number)
as
begin
merge into mergetest m using dual on (a = xa)
when not matched then insert (a,b) values (xa,1)
when matched then update set b = b+1;
end ups;
/
drop table mergetest;
create table mergetest(a number, b number);
call ups(10);
call ups(10);
call ups(20);
select * from mergetest;
A B
---------------------- ----------------------
10 2
20 1
The dual example above which is in PL/SQL was great becuase I wanted to do something similar, but I wanted it client side...so here is the SQL I used to send a similar statement direct from some C#
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" )
However from a C# perspective this provide to be slower than doing the update and seeing if the rows affected was 0 and doing the insert if it was.
An alternative to MERGE (the "old fashioned way"):
begin
insert into t (mykey, mystuff)
values ('X', 123);
exception
when dup_val_on_index then
update t
set mystuff = 123
where mykey = 'X';
end;
Another alternative without the exception check:
UPDATE tablename
SET val1 = in_val1,
val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%rowcount = 0 )
THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
insert if not exists
update:
INSERT INTO mytable (id1, t1)
SELECT 11, 'x1' FROM DUAL
WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11);
UPDATE mytable SET t1 = 'x1' WHERE id1 = 11;
None of the answers given so far is safe in the face of concurrent accesses, as pointed out in Tim Sylvester's comment, and will raise exceptions in case of races. To fix that, the insert/update combo must be wrapped in some kind of loop statement, so that in case of an exception the whole thing is retried.
As an example, here's how Grommit's code can be wrapped in a loop to make it safe when run concurrently:
PROCEDURE MyProc (
...
) IS
BEGIN
LOOP
BEGIN
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" );
EXIT; -- success? -> exit loop
EXCEPTION
WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
NULL; -- exception? -> no op, i.e. continue looping
WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
NULL; -- exception? -> no op, i.e. continue looping
END;
END LOOP;
END;
N.B. In transaction mode SERIALIZABLE, which I don't recommend btw, you might run into
ORA-08177: can't serialize access for this transaction exceptions instead.
I'd like Grommit answer, except it require dupe values. I found solution where it may appear once: http://forums.devshed.com/showpost.php?p=1182653&postcount=2
MERGE INTO KBS.NUFUS_MUHTARLIK B
USING (
SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO
FROM DUAL
) E
ON (B.MERNIS_NO = E.MERNIS_NO)
WHEN MATCHED THEN
UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK
WHEN NOT MATCHED THEN
INSERT ( CILT, SAYFA, KUTUK, MERNIS_NO)
VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO);
I've been using the first code sample for years. Notice notfound rather than count.
UPDATE tablename SET val1 = in_val1, val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%notfound ) THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
The code below is the possibly new and improved code
MERGE INTO tablename USING dual ON ( val3 = in_val3 )
WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2
WHEN NOT MATCHED THEN INSERT
VALUES (in_val1, in_val2, in_val3)
In the first example the update does an index lookup. It has to, in order to update the right row. Oracle opens an implicit cursor, and we use it to wrap a corresponding insert so we know that the insert will only happen when the key does not exist. But the insert is an independent command and it has to do a second lookup. I don't know the inner workings of the merge command but since the command is a single unit, Oracle could execute the correct insert or update with a single index lookup.
I think merge is better when you do have some processing to be done that means taking data from some tables and updating a table, possibly inserting or deleting rows. But for the single row case, you may consider the first case since the syntax is more common.
A note regarding the two solutions that suggest:
1) Insert, if exception then update,
or
2) Update, if sql%rowcount = 0 then insert
The question of whether to insert or update first is also application dependent. Are you expecting more inserts or more updates? The one that is most likely to succeed should go first.
If you pick the wrong one you will get a bunch of unnecessary index reads. Not a huge deal but still something to consider.
Try this,
insert into b_building_property (
select
'AREA_IN_COMMON_USE_DOUBLE','Area in Common Use','DOUBLE', null, 9000, 9
from dual
)
minus
(
select * from b_building_property where id = 9
)
;
From http://www.praetoriate.com/oracle_tips_upserts.htm:
"In Oracle9i, an UPSERT can accomplish this task in a single statement:"
INSERT
FIRST WHEN
credit_limit >=100000
THEN INTO
rich_customers
VALUES(cust_id,cust_credit_limit)
INTO customers
ELSE
INTO customers SELECT * FROM new_customers;
I am working with TSP(Traveling salesmen problem) Solution in mysql.
for that we are going to develop procedure where i want to execute following query
-- variable coming from java
CREATE PROCEDURE solve_tsp(
inout ids varchar(21845);
)
begin
declare ret_ids varchar(21845);
-- some code
while length(ids) > 0 do
-- some assignment
SELECT to_id,distance into #l_tmp_id,#l_distance FROM mst_distance_matrix WHERE from_id =',#l_tmp_id,' AND
to_id IN (ids) -- ids is variable contains comma delimited string.
order by distance desc limit 1
-- concat value of to_id in ret_ids variable and then remove from ids variable
end while;
select ret_ids;
end;
You should look here.
http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-prepared-statements.html
Example:
PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
SET #a = 3;
SET #b = 4;
EXECUTE stmt1 USING #a, #b;
DEALLOCATE PREPARE stmt1;
See also:
Dynamic Query in MySQL
We recently fixed an issue with the character encoding being read incorrectly into our system from the text files by making sure the file is UTF-8 and the Java code opens these files in UTF-8 encoding.
However, we had ended up adding a lot of records across the entire database tables with incorrect characters being inserted i.e. °F was read as �F. So even though we have fixed this now, we need to clean up the database tables now to rectify this anomaly.
Can anyone please suggest me ways to achieve this?
I had a similar problem a while back. Luckily, the number of columns that it affected was limited to a small number, and those columns had the same name throughout the database.
I solved this by writing a script that does the following:
disable foreign key constraints
build up a list of tables which contain the
target columns
update all the tables in your list using a
REGEXP_REPLACE
commit the data re-enable the constraints
This used a healthy dose of dynamic SQL, pulling data from the user_constraints and user_tab_columns, filtering on the specific column names I was targeting.
Here's a rough skeleton to get you started, I've just thrown it together quickly, so it isn't tested. Also, if you have triggers to worry about, you'll need to disable those too:
-- disable constraints
BEGIN
FOR c IN (
SELECT c.owner, c.table_name, c.constraint_name, c.constraint_type
FROM user_constraints c
INNER JOIN user_tables t ON (t.table_name = c.table_name)
AND c.status = 'ENABLED'
AND c.constraint_type NOT IN ('C', 'P')
ORDER BY c.constraint_type DESC
)
LOOP
dbms_utility.exec_ddl_statement('alter table '||c.table_name||' disable constraint ' || c.constraint_name);
END LOOP;
END;
-- do the updates
BEGIN
FOR t IN (
SELECT table_name, column_name
FROM user_tab_columns
WHERE column_name = 'TEMPERATURE'
AND data_type = 'VARCHAR2';
)
LOOP
dbms_utility.exec_ddl_statement('UPDATE '||t.table_name||' SET ' ||t.column_name||' = '||''GOOD VALUE''||' WHERE '||t.column_name||' = '||''BAD VALUE'');
END LOOP;
END;
-- re-enable constraints
BEGIN
FOR c IN (
SELECT c.owner, c.table_name, c.constraint_name, c.constraint_type
FROM user_constraints c
INNER JOIN user_tables t ON (t.table_name = c.table_name)
AND c.status = 'DISABLED'
AND c.constraint_type NOT IN ('C', 'P')
ORDER BY c.constraint_type ASC
)
LOOP
dbms_utility.exec_ddl_statement('alter table '||c.table_name||' enable constraint ' || c.constraint_name);
END LOOP;
END;
/