UPDATE/INSERT statement within an IF statement within a LOOP using sequence - sql

CURSOR text IS
SELECT *
FROM DATA
CURSOR MATCHES IS
SELECT NAME
FROM DATA
INTERSECT
SELECT DESCRIPTION
FROM my_table
BEGIN
FOR i IN text
OPEN MATCHES
FETCH MATCHES INTO MATCH
CLOSE MATCH
IF i IN MATCH
THEN
UPDATE my_table
SET col1 = correlating_new_column1, col2 = correlating_new_column2, col3 = correlating_new_column3
WHERE table_im_trying_to_populate.code = my_seq.curval
ELSE
INSERT INTO TABLE_IM_TRYING_TO_POPULATE(CODE, NAME, DESCRIPTION, col1, col2, col3)
VALUES(my_seq.nextval, other_name, other_description, correlating_new_column1, correlating_new_column2, correlating_new_column3)
END IF;
END LOOP;
Basically I am trying to take an explicit cursor I made that is a select statement of a table and then do a line by line loop of that and put it into my other existing table. If it comes across a name in my other exisiting table it will update some of the columns. Else it inserts the whole record into that table. I am attempting to use sequence to update the 'code' column so that it updates where the code from the other existing table = my_seq.curval. Then for the inset it just goes to the next val. I know this is complicated but Im really just trying to see if I have the setup correct. Just started using sql developer for oracle not to long ago.

There are a huge number of problems with your code. However, it looks like what you're trying to do can be achieved with a single MERGE statement, along the lines of:
merge into table_im_trying_to_populate tgt
using data_table src
on (tgt.name = src.other_name
and tgt.description = src.other_description)
when matched then
update set tgt.col1 = src.correlating_new_column1,
tgt.col2 = src.correlating_new_column2,
tgt.col3 = src.correlating_new_column3
when not matched then
insert (tgt.code, tgt.name, tgt.description, tgt.col1, tgt.col2, tgt.col3)
values (my_seq.nextval, src.other_name, src.other_description, src.correlating_new_column1, src.correlating_new_column2, src.correlating_new_column3);
This assumes that the other_name and other_description columns in the data table is unique. I've also had to guess at what the join condition should be, since the join condition you had in your example update statement (table_im_trying_to_populate.code = my_seq.currval) didn't make any sense - you don't use currval to join against as a general rule, since it isn't populated unless you've previously pulled a value from the sequence in the same session.
If this doesn't match what you're trying to do, please update your question with some sample data in both tables and the expected output, and we should be able to help you further.

Related

Updating or inserting SQL Server destination table data based on conditions from source table data

I have two SQL Server tables, one is my destination table (LocaleStringResource), the other one is the source table (TempResourceSunil).
Source table has the following columns: TempResourceSunil
[ID], [LanguageId], [ResourceName], [ResourceValue], [Burmese], [Unicode]
and the destination table's columns are LocaleStringResource
[Id], [LanguageId], [ResourceName], [ResourceValue]
I want to update the destination table [ResourceValue] based on [ResourceName] from the source file.
Example:
[ResourceName] = 'Account.AccountActivation'
means I want to check it have corresponding Burmese [ResourceValue] in LocaleStringResource table if it does not exist, I will take it from TempResourceSunil and Burmese column and insert it into LocaleStringResource with language id =2.
Same if [ResourceValue] for Unicode (language id = 3) does not exist for [ResourceName] = 'Account.AccountActivation' means I want to insert [ResourceValue] from TempResourceSunil with language id = 3.
Can any SQL expert help me?
The description you gave isn't really fleshed out however, you want to use a Case Statement. CASE STATEMENT INFO
A case statement can have multiple WHENs to cover multiple logic statements. You can even have one inside the other. I wouldn't really do that for this situation.
The example below is just a simple version.
If l.[ResourceValue] is null and l.[ResourceName] = 'Account.AccountActivation' then use the value of T.[Burmese] for column l.[ResourceValue]. ELSE means if no When within the case statement is true, then use this value.
Also be aware that if you are trying to use an INT value from the first table in a string column on the 2nd, you need to cast it as a varchar.
Test out your logic and case statements and see how you get on.
SELECT
l.[Id],
l.[LanguageId],
l.[ResourceName],
CASE WHEN l.[ResourceName] = 'Account.AccountActivation' and l.[ResourceValue] is null then T.[Burmese]
else l.[ResourceValue] end as [ResourceValue],
T.[ID],
T.[LanguageId],
T.[ResourceName],
T.[ResourceValue],
T.[Burmese],
T.[Unicode]
FROM LocaleStringResource as L
LEFT JOIN TempResourceSunil t on (t.ID = L.ID) and (t.[LanguageId] = l.[LanguageId])

DB2 SQL Statement for Inserting new data and updating existing data

I've found a lot of near misses on this question. So many similar, but not quite right, scenarios. No doubt my ignorance will shine here.
Using DB2 and a shred of knowledge, my scenario is as follows:
On a table, insert a row of data if a given value is not present in a given column, or update the corresponding row if the value is present.
I have a table
id, bigint, not nullable
ref,varchar, nullable
I am not sure if a MERGE is the correct path here as most examples and thorough discussions all seem to revolve around merging one table into another. I'm simply gathering user input and either adding it or updating it. It seems like it should be really simple.
I'm using jdbc and prepared statements to get this done.
Is MERGE the correct way to do this?
When testing my query in DB2 Control Center, I run up against
"No row was found for FETCH, UPDATE or DELETE; or the result of a
query is an empty table"
or a variety of other errors depending on how I structure my MERGE. Here's what I have presently.
merge into table1 as t1
using (select id from table1 group by id) as t2
on t1.id = t2.id
when matched then update set t1.ref = 'abc'
when not matched then insert (t1.id, t1.ref) values (123, 'abc');
If I were to instead compose an update followed by an insert; for new data the insert runs and the update fails, and for existing data they both succeed resulting in bad data in the table, e.g. two identical rows.
The desired result is if on initial use with the values:
id = 1
ref = a
a new row is added. On subsequent use if the values change to:
id = 1
ref = b
the row with id = 1 is updated. Subsequent uses would follow the same rules.
Please let me know how I can phrase this question better.
Update id is not an automatic incrementing key. It's an external key that will be unique but not every thing we are referencing will need a related row in the table I'm attempting to update. This table is rather unstructured on its own but is part of a larger data model.
I'm a bit puzzled by your query. Reading the text makes me suspect that you want something like this:
merge into table1 as t1
using ( values (123, 'abc') ) as t2 (id, ref)
on t1.id = t2.id
when matched then update
set t1.ref = t2.ref
when not matched then
insert (id, ref) values (t2.id, t2.ref);
Is that correct?

Having a same temp table name with 2 different IF statements

I have resolved this problem because I have overlooked something that is already part of my code and this situation is not needed.
In SQL Server 2008, I have two IF statements
If value = ''
begin
select * into #temptable from table 1
end
Else If value <> ''
begin
select * into #temptable from table 2
end
but when I try to execute it gives me because of the second
temptable:
There is already an object named '#temptable' in the database.
I don't want to use another temp table name as I would have to change the after code a lot. Is there a way to bypass this?
I would recommend making some changes so that your code is a little more maintainable. One problem with the way you have it set up here is with the SELECT * syntax you're using. If you later decide to make a change to the schema of table1 or table2, you could have non-obvious consequences. In production code, it's better to spell these things out so that it's clear exactly which columns you're using and where.
Also, are you really using all of the columns from table 1 and table 2 in the code that follows? You might be taking a performance hit loading more data than you need. I'd go through the code that uses #temptable and figure out which columns it's actually using. Then start by creating your temp table:
CREATE TABLE #temptable(col1 int, col2 int, col3 int, col4 int)
Include all of the possible columns that could be used, even if some of them might be null in certain cases. Presumably, the code that follows already understands that. Then you can set up your IF statements:
IF value = ''
BEGIN
INSERT INTO #temptable(col1, col2, col3)
SELECT x,y,z
FROM table1
END
ELSE
INSERT INTO #temptable(col1, col4)
SELECT alpha,beta
FROM table2
END
Your SELECT statement, as written, is creating the temp table and INSERTING into it all in one statement. Create the temp table separately with a CREATE TABLE statement, then INSERT INTO in your two IF statements.
Using SELECT INTO creates the table on the fly, as you know. Even if your query only referenced #temptable once, if you were to run it more than once (without dropping the table after the first run), you would get the same error (although if it were inside a stored procedure, it would probably only exist in the scope of the stored procedure).
However, you can't even compile this query. Using the Parse command (Ctrl+F5) on the following query, for example, fails even though the same table is used as the source table.
select * into #temptable from SourceTable
select * into #temptable from SourceTable
If the structure of tables 1 and 2 were the same, you could do something like the following.
select * into #temptable from
(select * from Table1 where #value = ''
union
select * from Table2 where #value <> '') as T
If, however, the tables have different structures, then I'm not sure what you can do, other than what agt and D. Lambert recommended.

Update if different/changed

Is it possible to perform an update statement in sql, but only update if the updates are different?
for example
if in the database, col1 = "hello"
update table1 set col1 = 'hello'
should not perform any kind of update
however, if
update table1 set col1 = "bye"
this should perform an update.
During query compilation and execution, SQL Server does not take the time to figure out whether an UPDATE statement will actually change any values or not. It just performs the writes as expected, even if unnecessary.
In the scenario like
update table1 set col1 = 'hello'
you might think SQL won’t do anything, but it will – it will perform all of the writes necessary as if you’d actually changed the value. This occurs for both the physical table (or clustered index) as well as any non-clustered indexes defined on that column. This causes writes to the physical tables/indexes, recalculating of indexes and transaction log writes. When working with large data sets, there is huge performance benefits to only updating rows that will receive a change.
If we want to avoid the overhead of these writes when not necessary we have to devise a way to check for the need to be updated. One way to check for the need to update would be to add something like “where col <> 'hello'.
update table1 set col1 = 'hello' where col1 <> 'hello'
But this would not perform well in some cases, for example if you were updating multiple columns in a table with many rows and only a small subset of those rows would actually have their values changed. This is because of the need to then filter on all of those columns, and non-equality predicates are generally not able to use index seeks, and the overhead of table & index writes and transaction log entries as mentioned above.
But there is a much better alternative using a combination of an EXISTS clause with an EXCEPT clause. The idea is to compare the values in the destination row to the values in the matching source row to determine if an update is actually needed. Look at the modified query below and examine the additional query filter starting with EXISTS. Note how inside the EXISTS clause the SELECT statements have no FROM clause. That part is particularly important because this only adds on an additional constant scan and a filter operation in the query plan (the cost of both is trivial). So what you end up with is a very lightweight method for determining if an UPDATE is even needed in the first place, avoiding unnecessary write overhead.
update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists
(
/* DESTINATION */
select table1.col1
except
/* SOURCE */
select col1 = 'hello'
)
This looks overly complicated vs checking for updates in a simple WHERE clause for the simple scenerio in the original question when you are updating one value for all rows in a table with a literal value. However, this technique works very well if you are updating multiple columns in a table, and the source of your update is another query and you want to minimize writes and transaction logs entries. It also performs better than testing every field with <>.
A more complete example might be
update table1
set col1 = 'hello',
col2 = 'hello',
col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
and exists
(
/* DESTINATION */
select table1.col1
table1.col2
table1.col3
except
/* SOURCE */
select z.col1,
z.col2,
z.col3
from #anytemptableorsubquery z
where z.CustomerId = table1.CustomerId
)
The idea is to not perform any update if a new value is the same as in DB right now
WHERE col1 != #newValue
(obviously there is also should be some Id field to identify a row)
WHERE Id = #Id AND col1 != #newValue
PS: Originally you want to do update only if value is 'bye' so just add AND col1 = 'bye', but I feel that this is redundant, I just suppose
PS 2: (From a comment) Also note, this won't update the value if col1 is NULL, so if NULL is a possibility, make it WHERE Id = #Id AND (col1 != #newValue OR col1 IS NULL).
If you want to change the field to 'hello' only if it is 'bye', use this:
UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'
If you want to update only if it is different that 'hello', use:
UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'
Is there a reason for this strange approach? As Daniel commented, there is no special gain - except perhaps if you have thousands of rows with col1='hello'. Is that the case?
This is possible with a before-update trigger.
In this trigger you can compare the old with the new values and cancel the update if they don't differ. But this will then lead to an error on the caller's site.
I don't know, why you want to do this, but here are several possibilities:
Performance: There is no performance gain here, because the update would not only need to find the correct row but additionally compare the data.
Trigger: If you want the trigger only to be fired if there was a real change, you need to implement your trigger like so, that it compares all old values to the new values before doing anything.
CREATE OR REPLACE PROCEDURE stackoverflow([your_value] IN TYPE) AS
BEGIN
UPDATE [your_table] t
SET t.[your_collumn] = [your_value]
WHERE t.[your_collumn] != [your_value];
COMMIT;
EXCEPTION
[YOUR_EXCEPTION];
END stackoverflow;
You need an unique key id in your table, (let's suppose it's value is 1) to do something like:
UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"
Old question but none of the answers correctly address null values.
Using <> or != will get you into trouble when comparing values for differences if there are is potential null in the new or old value to safely update only when changed use the is distinct from operator in Postgres. Read more about it here
I think this should do the trick for ya...
create trigger [trigger_name] on [table_name]
for insert
AS declare #new_val datatype,#id int;
select #new_val = i.column_name from inserted i;
select #id = i.Id from inserted i;
update table_name set column_name = #new_val
where table_name.Id = #id and column_name != #new_val;

select the rows affected by an update

If I have a table with this fields:
int:id_account
int:session
string:password
Now for a login statement I run this sql UPDATE command:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple'
Then I check if a row was affected, and if one indeed was affected I know that the password was correct.
Next what I want to do is retrieve all the info of this affected row so I'll have the rest of the fields info.
I can use a simple SELECT statement but I'm sure I'm missing something here, there must be a neater way you gurus know, and going to tell me about (:
Besides it bothered me since the first login sql statement I ever written.
Is there any performance-wise way to combine a SELECT into an UPDATE if the UPDATE did update a row?
Or am I better leaving it simple with two statements? Atomicity isn't needed, so I might better stay away from table locks for example, no?
You should use the same WHERE statement for SELECT. It will return the modified rows, because your UPDATE did not change any columns used for lookup:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple';
SELECT *
FROM tbl_name
WHERE id_account = 17 AND password = 'apple';
An advice: never store passwords as plain text! Use a hash function, like this:
MD5('apple')
There is ROW_COUNT() (do read about details in the docs).
Following up by SQL is ok and simple (which is always good), but it might unnecessary stress the system.
This won't work for statements such as...
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value From Table
Where Value is Null
You would have changed the value with the update and would be unable to recover the affected records unless you stored them beforehand.
Select * Into #TempTable
From Table
Where Value is Null
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value, UniqueValue
From #TempTable TT
Join Table T
TT.UniqueValue = T.UniqueValue
If you're lucky, you may be able to join the temp table's records to a unique field within Table to verify the update. This is just one small example of why it is important to enumerate records.
You can get the effected rows by just using ##RowCount..
select top (Select ##RowCount) * from YourTable order by 1 desc