Condtional statement execution in sqlite - sql

New to SQL and Sqlite, I'm afraid. I need to delete a row from table A, but only if that row isn't referenced in table B. So, I guess I need to do something like this:
delete from tableA
where (col1 = 94) and
(select count(*) from tableB (where col2 = 94) = 0);
What's the right way to do this?
Alternatively, I could just do this from C in two steps, first checking that the row isn't referenced, and then deleting it. Would this be better? I'm hesitant to do this because I would need to put an sqlite3_mutex around several steps in the C code, which might be less efficient that executing a single more complex statement. Thanks.

Your method is pretty close. The parentheses are in the wrong place:
delete from tableA
where (col1 = 94) and
(select count(*) from tableB where col2 = 94) = 0;
For instance, SQL doesn't allow a paren before the where.
I would however suggest that you learn about foreign keys. These are a useful construct in SQL. The SQLite documentation is here.
A foreign key constraint would have the database do this check automatically whenever a row is being deleted from tableA. The delete would return an error, but it would make sense -- something like "you can't delete this row because another row references it".

delete tableA
from tableA
left join tableB on tableA.col1 = tableB.col2
where tableB.col2 is null
and tableA.col1 = 94
You can left join the two tables and delete only those where the link could not be established between them (tableB.col2 is null).
Alternatively you can do
delete from tableA
where col1 = 94
and not exists
(
select 1 from tableB where col2 = 94
)

Related

Updating a column with maximum matches from another table

I have two tables let’s say A & B and would like to update the column of Status in table A with maximum matches from the column Scores in table B by comparing the 'Topics' of two tables.
I am using the script shown here, but it's taking a really long time so I'd appreciate it if somebody could provide an alternative / better and faster option/script
UPDATE tableA
SET status = (SELECT max(scores)
FROM tableB
WHERE tableB.topics = tableA.topics)
Try creating proper indexes for each column involved and you should be fine, e.g:
CREATE INDEX idx_tableb_topics_scores ON tableb (topics,scores);
An alternative to your query is to apply the aggregate function max() in a way that it only has to be executed once, but I doubt it will speed things up:
UPDATE tablea a SET status = j.max_scores
FROM (SELECT a.topics,max(b.scores) AS max_scores
FROM tablea a
JOIN tableb b ON a.topics = b.topics
GROUP BY a.topics) j
WHERE a.topics = j.topics;
For this query:
UPDATE tableA
SET status = (SELECT max(scores)
FROM tableB
WHERE tableB.topics = tableA.topics
);
The only index you need is on tableB(topics, scores).
If you like, you can rewrite this as an aggregation, which looks like this:
UPDATE tableA
SET status = b.max_scores
FROM (SELECT b.topic, MAX(scores) as max_scores
FROM tableB b
GROUP BY b.topic
) b
WHERE b.topic = a.topic;
Note that this is subtly different from your query. If there are topics in A that are not in B, then this will not update those rows. I do not know if that is desirable.
If many rows in A have the same topic, then pre-aggregating could be significantly faster than the correlated subquery.

Deleting records from a table without primary key

I need to delete some specific record from database table but table itself does not have primary key. So condition depends on other table. So what is the correct way to do that?
delete from table_1
where exists
(select distinct tb.*
from table_1 tb, table_2 tb_2, table_3 tb_3
where tb1.col = tb2.col
and tb3.col = tb2.col
and tb3.col_2= 10)
is that correct way to do that? Lets say table_1 has 4 columns and first two columns should be the criteria to remove.
If the select version of your query returns the results you want to delete, then you're good. A couple things though..
Use the ANSI compliant explicit join syntax instead of the comma delineated, implicit syntax (which is long since depreciated). The explicit syntax looks better and is easier to read, anyway.
Correlate your EXISTS back to the main table. And you don't need a distinct, it will return positive whether there is 1 matching row or 10 billion.
SELECT *
FROM table_1 tb_1
WHERE EXISTS (SELECT *
FROM table_2 tb_2
JOIN table_3 tb_3 ON tb_2.col = tb_3.col
WHERE tb_1.col = tb_2.col
AND tb_3.col_2 = 10)

Update using Inner Join throwing syntax error

I would like to update columns in Table A based on values in Table B. Using below format, but getting syntax error.
update TableA
set
TableA.MOdule_id =TableB.MOdule_id
from TableA
inner join
TableB
on TableA.end_Slot_id =TableB.Slot_Id
where TableA.Slot_Id = 'AAA';
It would be great help, if anyone can help on this.
A quick search for "informix sql update" returns two reference manual pages that describe the syntax for the UPDATE command. Neither one indicates support for the nonstandard FROM clause.
Standard SQL uses a correlated subquery for the purpose. Your query should look something like
update TableA
set MOdule_id =
(select TableB.MOdule_id
from TableB
on TableA.end_Slot_id = Slot_Id)
where Slot_Id = 'AAA'
and exists (
select 1
from TableB
on TableA.end_Slot_id = Slot_Id
and TableA.Slot_Id = 'AAA'
);
The EXISTS clause ensures that only rows that exist in B are applied to A. Without it, any missing rows would be updated to NULL.

Postgres: Update all columns from another table

I need to update a table from another one, and I need to update all columns, My questions is that, besides list every column in the SET, is there a way to update them all like this:
update tableA
set * = tableB.*
from tableB where tableA.id = tableB.id
I tried in psql, it doesn't work.. I have to list every column like this:
update tableA
set c1 = tableB.c1, c2 = tableB.c2, ...
from tableB where tableA.id = tableB.id
tableB is created use create..like tableA. So they are basically identical. And the reason I'm doing it is that I need load .csv data to temp table tableB and then update tableA based on the new data in tableB. tableA needs to be locked as little as possible and tableA needs to keep integrity. I'm not sure 'delete then insert' would be a good option?
Thanks!
You could delete and re-insert, if the two tables have the same columns in the same order. Assuming that all records in tableB match tableA:
delete from tableA
where id in (select id from tableB)
insert into tableA
select *
from tableB;
(If all records do not match, you could use a temporary table to keep the necessary ids.)
Generally, I oppose doing insert without a column list. In some cases, it is tolerable -- such as when tableB was created as a subset from tableB using *.
Depends on your use case and will cause locks but of a different type. Approach that works well for some scenarios.
BEGIN;
DROP TABLE IF EXISTS tableA_old;
ALTER tableA RENAME TO tableA_old;
ALTER tableB RENAME TO tableA;
COMMIT;

How to efficiently retrieve data in one to many relationships

I am running into an issue where I have a need to run a Query which should get some rows from a main table, and have an indicator if the key of the main table exists in a subtable (relation one to many).
The query might be something like this:
select a.index, (select count(1) from second_table b where a.index = b.index)
from first_table a;
This way I would get the result I want (0 = no depending records in second_table, else there are), but I'm running a subquery for each record I get from the database. I need to get such an indicator for at least three similar tables, and the main query is already some inner join between at least two tables...
My question is if there is some really efficient way to handle this. I have thought of keeping record in a new column the "first_table", but the dbadmin don't allow triggers and keeping track of it by code is too risky.
What would be a nice approach to solve this?
The application of this query will be for two things:
Indicate that at least one row in second_table exists for a given row in first_table. It is to indicate it in a list. If no row in the second table exists, I won't turn on this indicator.
To search for all rows in first_table which have at least one row in second_table, or which don't have rows in the second table.
Another option I just found:
select a.index, b.index
from first_table a
left join (select distinct(index) as index from second_table) b on a.index = b.index
This way I will get null for b.index if it doesn' exist (display can finally be adapted, I'm concerned on query performance here).
The final objective of this question is to find a proper design approach for this kind of case. It happens often, a real application culd be a POS system to show all clients and have one icon in the list as an indicator wether the client has open orders.
Try using EXISTS, I suppose, for such case it might be better then joining tables. On my oracle db it's giving slightly better execution time then the sample query, but this may be db-specific.
SELECT first_table.ID, CASE WHEN EXISTS (SELECT * FROM second_table WHERE first_table.ID = second_table.ID) THEN 1 ELSE 0 END FROM first_table
why not try this one
select a.index,count(b.[table id])
from first_table a
left join second_table b
on a.index = b.index
group by a.index
Two ideas: one that doesn't involve changing your tables and one that does. First the one that uses your existing tables:
SELECT
a.index,
b.index IS NOT NULL,
c.index IS NOT NULL
FROM
a_table a
LEFT JOIN
b_table b ON b.index = a.index
LEFT JOIN
c_table c ON c.index = a.index
GROUP BY
a.index, b.index, c.index
Worth noting that this query (and likely any that resemble it) will be greatly helped if b_table.index and c_table.index are either primary keys or are otherwise indexed.
Now the other idea. If you can, instead of inserting a row into b_table or c_table to indicate something about the corresponding row in a_table, indicate it directly on the a_table row. Add exists_in_b_table and exists_in_c_table columns to a_table. Whenever you insert a row into b_table, set a_table.exists_in_b_table = true for the corresponding row in a_table. Deletes are more work since in order to update the a_table row you have to check if there are any rows in b_table other than the one you just deleted with the same index. If deletes are infrequent, though, this could be acceptable.
Or you can avoid join altogether.
WITH comb AS (
SELECT index
, 'N' as exist_ind
FROM first_table
UNION ALL
SELECT DISTINCT
index
, 'Y' as exist_ind
FROM second_table
)
SELECT index
, MAX(exist_ind) exist_ind
FROM comb
GROUP BY index
The application of this query will be for two things:
Indicate that at least one row in second_table exists for a given row in first_table. It is to indicate it in a list.
To search for all rows in first_table which have at least one row in second_table.
Here you go:
SELECT a.index, 1 as c_check -- 1: at least one row in second_table exists for a given row in first_table
FROM first_table a
WHERE EXISTS
(
SELECT 1
FROM second_table b
WHERE a.index = b.index
);
I am assuming that you can't change the table definitions, e.g. partitioning the columns.
Now, to get a good performance you need to take into account other tables which are getting joined to your main table.
It all depends on data demographics.
If the other joins will collapse the rows by high factor, you should consider doing a join between your first table and second table. This will allow the optimizer to pick best join order , i.e, first joining with other tables then the resulting rows joined with your second table gaining the performance.
Otherwise, you can take subquery approach (I'll suggest using exists, may be Mikhail's solution).
Also, you may consider creating a temporary table, if you need such queries more than once in same session.
I am not expert in using case, but will recommend the join...
that works even if you are using three tables or more..
SELECT t1.ID,t2.name, t3.date
FROM Table1 t1
LEFT OUTER JOIN Table2 t2 ON t1.ID = t2.ID
LEFT OUTER JOIN Table3 t3 ON t2.ID = t3.ID
--WHERE t1.ID = #ProductID -- this is optional condition, if want specific ID details..
this will help you fetch the data from Normalized(BCNF) tables.. as they always categorize data with type of nature in separate tables..
I hope this will do...