Insert into Oracle SQL or Update if value already exist? - c#

What I want is: If key exist in Oracle SQL Table → UPDATE, otherwise do a INSERT
After playing all day yesterday, I managed to get the INSERT part (query below), what I now need is the UPDATE part.
This is why I need it: I have a RadGridView (using Telerik and WPF). When a User clicks on Insert button a new Row is added and after the User hits Enter, the value is Inserted into the Database. But the user can also click on a available row in the RadGridView and update the values, so I need to update the values in my Database too (and not do a INSERT). And that is why I want a query that can perform a Udate or Insert.
Any help is appreciated, also if you have a better way of implementing the INSERT part, I would like to know about it :)
I am using Oracle SQL Developer (Windows 10).
ps. I tried some solutions I found on StackOverflow but could not manage to apply the solutions to my problem.
Table1
+-----------------------------------+------------+--------+
| ID | ORIGTERM | CODE |
+-----------------------------------+------------+--------+
| 126478 | Grass | TEST |
| 374628 | Flower | TEST |
| 128754 | Tree | TEST |
+-----------------------------------+------------+--------+
Table2
+-----------------------------------+------------+---------+
| ID |REPLACETERM |SYSCTLANG|
+-----------------------------------+------------+---------+
| 126478 | Gras | 3 |
| 374628 | Blume | 3 |
| 128754 | Baum | 3 |
+-----------------------------------+------------+---------+
I managed to got the INSERT query, it looks like this (example insert the word 'Plant'):
INSERT ALL
INTO Table1(origterm,code) VALUES (s_origterm,s_code)
INTO Table2(replaceterm) VALUES (s_replaceterm)
SELECT s_origterm, s_code, s_replaceterm
FROM (SELECT 'Plant' s_origterm, 'TEST' s_code, 'Pflanze' s_replaceterm FROM dual)
dual;
I must also update the ID of Table2 to be the same as the one from Table1:
UPDATE Table2 SET Table2.ID = (SELECT Table1.ID FROM Table1 WHERE origterm='Plant')
WHERE replaceterm='Pflanze';
Now I have a table that looks like this:
+-----------------------------------+------------+--------------+------+
| ID | ORIGTERM | REPLACETERM | CODE |
+-----------------------------------+------------+--------------+------+
| 126478 | Grass | Gras | TEST |
| 374628 | Flower | Blume | TEST |
| 128754 | Tree | Baum | TEST |
| 100000 | Plant | Pflanze | TEST |
+-----------------------------------+------------+--------------+------+
SELECT g.ID, origterm, replaceterm, code FROM Table1 g, Table2 ct WHERE g.ID = ct.ID;

Merge does not work with INSERT ALL. If you want to use merge, you should make from both table a view with instead of trigger and than use merge against the view. The whole logic will be inside the trigger.
EDIT: Merge does not work with such views
ORA-38106: MERGE bei Join View oder View mit INSTEAD OF-Trigger nicht unterstützt
You can make two merge statements (one for every table) or one for insert and one for update against view:
CREATE SEQUENCE MYDICT_SEQ START WITH 1 MAXVALUE 9999999999999999999999999999 MINVALUE 0;
CREATE VIEW mydict
AS
SELECT a.id, a.origterm, a.code, b.replaceterm, b.sysctlang
FROM table1 a LEFT OUTER JOIN table2 b ON a.id = b.id;
CREATE OR REPLACE TRIGGER mydict_io
INSTEAD OF INSERT OR UPDATE OR DELETE
ON mydict
FOR EACH ROW
DECLARE
cnt1 INTEGER := 0;
cnt2 INTEGER;
nid NUMBER;
BEGIN
IF INSERTING OR UPDATING THEN
IF :new.id IS NULL AND INSERTING THEN
nid := mydict_seq.NEXTVAL;
ELSE
nid := :new.id;
IF UPDATING THEN
nid := :old.id;
END IF;
SELECT COUNT (*)
INTO cnt1
FROM table1
WHERE id = nid;
END IF;
IF cnt1 = 0 THEN
INSERT INTO TABLE1 (ID, ORIGTERM, CODE)
VALUES (nID, :new.ORIGTERM, :new.CODE);
ELSIF cnt1 > 0 THEN
UPDATE TABLE1
SET ORIGTERM = :NEW.ORIGTERM, CODE = :NEW.CODE
WHERE id = nid;
END IF;
SELECT COUNT (*)
INTO cnt2
FROM table2
WHERE id = nid AND SYSCTLANG = :new.SYSCTLANG;
IF cnt2 = 0 THEN
INSERT INTO TABLE2 (ID, REPLACETERM, SYSCTLANG)
VALUES (nID, :new.REPLACETERM, :new.SYSCTLANG);
ELSE
UPDATE TABLE2
SET REPLACETERM = :new.REPLACETERM
WHERE id = nid AND SYSCTLANG = :new.SYSCTLANG;
END IF;
ELSIF DELETING THEN
DELETE FROM table2
WHERE id = :old.id AND SYSCTLANG = :old.SYSCTLANG;
SELECT COUNT (*)
INTO cnt2
FROM table2
WHERE id = nid;
IF cnt2 = 0 THEN
DELETE FROM table1
WHERE id = :old.id;
END IF;
END IF;
END;
DECLARE
nid NUMBER;
BEGIN
INSERT INTO mydict (ORIGTERM, CODE, REPLACETERM, SYSCTLANG)
VALUES ('Plant', 'TEST', 'Pflanze', 3);
nid := mydict_seq.currval;
UPDATE mydict
SET REPLACETERM = 'Fabrik'
WHERE id = nid;
UPDATE mydict
SET REPLACETERM = 'Usine', SYSCTLANG = 4
WHERE id = nid;
END;
The logic in the instead of trigger can be done better, but I leave this to you ;)

maybe it's too obvious but if you own the 'key' value probably this procedure will work:
DECLARE _flag AS INT = 0;
SELECT COUNT() INTO _flag FROM table1 WHERE ID = key;
IF _flag = 0 THEN
INSERT ...
ELSE
UPDATE ....
END IF;
adjust it to your own code needs and/or ORACLE SQL dialect.

A possible solution is to use the MERGE keyword, documented here:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm
Merge is basically an 'upsert' command, meaning that it will update row if exists or insert, if it doesn't.

Related

Remove need for second T-SQL query

I am loading some data into a repeater which is coming from two tables. The query against the second table is only selecting the MAX record though, and because of this complexity, I'm having to create a child repeater to then go off and find the Max record to display.
Table A: Activity List
ID | Activity
----+-----------------------
1 | Change Oil Filter
2 | Change brake fluid
3 | Change brake rotors
Table B: Mechanics Log
ID | ActivityID | Date | Mechanic | Comment
---+-------------+-------------+-------------------------------------------
1 | 1 | 2019-27-06 | John | Changed the oil filter
2 | 1 | 2019-26-06 | Sally | No oil filters in stock.
3 | 2 | 2019-20-06 | Sally | Brake fluid flushed.
As stated above, I can produce the following table using two repeaters (one inside the other) and it looks like this.
ActivityID | Date | Mechanic | Comment
-------------+-------------+-----------------------------------------
1 | 2019-27-06 | John | Changed the oil filter
2 | 2019-20-06 | Sally | Brake fluid flushed.
3 | | |
My question is: How can I produce the same table but using only one repeater and 1 T-SQL query? Is it possible? The reason being is that this is a very simple list (shortened for this demonstration) of the full list I have to enable for my mechanics work log, and when i start going to 100+ activities that can be done on a vehicle, the page loads quite slow; assuming because it has to fire off the 2nd repeater + code for each record it has bound.
I also apologize I do not yet have a 'starting point' for you to work with, as nothing I have created has come even close to producing the result in one query. I am having trouble working out how I combine the first part of the query with the MAX(Date) of the 2nd table. Hoping for some assistance from the community to help.
You can use the below query to get the desired result -
Sample Data
Declare #ActivityList Table
(ID int, Activity varchar(100))
Insert into #ActivityList
values
(1 , 'Change Oil Filter' ),
(2 , 'Change brake fluid' ),
(3 , 'Change brake rotors' )
Declare #MechanicsLog Table
(ID int, ActivityID int, [Date] Date, Mechanic varchar(20), Comment varchar(50))
Insert into #MechanicsLog
values
(1 , 1 , '2019-06-27' , 'John' , 'Changed the oil filter' ),
(2 , 1 , '2019-06-26' , 'Sally' , 'No oil filters in stock.' ),
(3 , 2 , '2019-06-20' , 'Sally' , 'Brake fluid flushed.' )
Query
;With cte as
(select ActivityID, Max([Date]) [date] from #MechanicsLog ml
Group By ActivityID
)
Select al.ID, al.Activity, cte.[Date], Mechanic, Comment
from cte inner join #MechanicsLog ml
on cte.ActivityID = ml.ActivityID and cte.[date] = ml.[Date]
right join #ActivityList al on al.ID = ml.ActivityID
order by ID
If you add use the ROW_NUMBER function to add a sequence to each activity ID, you can then filter that to only get the most recent for each activity ID.
select ActivityID, Date, Mechanic, Comment
from
(
select *, ROW_NUMBER() OVER (PARTITION BY ActivityID order by Date desc) RowNumber
from MechanicsLog
) q1
where RowNumber = 1
This gives you the "MAX" record for each ActivityID but with the rest of the record, so you can join to the Activity List table if you want.
select
act.ActivityID, Max(log.[Date]) as [Date]
from
ActivityList act
inner join
MachineLog log on log.ActivityID = act.ActivityID
Group by
act.ActivityID

function set table name as parameter and get columns name and table name from XML file in sqlcommand c# [duplicate]

Here is my SQL query below. I want to select values from the column names given as variables. Is there any appropriate way of doing this except using a dynamic query?
SELECT EPV.EmployeeCode, #RateOfEmployee, #RateOfEmployer
FROM [HR_EmployeeProvisions] EPV
One way to do this without using dynamic sql is using CASE statement
But this is ugly
SELECT EPV.EmployeeCode, case #RateOfEmployee when 'RateOfEmployee' then RateOfEmployee
when 'X' then X
..
end , case #RateOfEmployer when 'RateOfEmployer' then RateOfEmployer
when 'Y' then Y
..
end
FROM [HR_EmployeeProvisions] EPV
You have to check all the column's in CASE statement.
You can't parameterize identifiers in Sql server, and I doubt it's possible in any other relational database.
Your best choice is to use dynamic Sql.
Note that dynamic sql is very often a security hazard and you must defend your code from sql injection attacks.
I would probably do something like this:
Declare #Sql nvarchar(500)
Declare numberOfColumns int;
select #numberOfColumns = count(1)
from information_schema.columns
where table_name = 'HR_EmployeeProvisions'
and column_name IN(#RateOfEmployee, #RateOfEmployer)
if #numberOfColumns = 2 begin
Select #Sql = 'SELECT EmployeeCode, '+ QUOTENAME(#RateOfEmployee) +' ,'+ QUOTENAME(#RateOfEmployer) +
'FROM HR_EmployeeProvisions'
exec(#Sql)
end
This way you make sure that the column names actually exists in the table, as well as using QUOTENAME as another layer of safety.
Note: in your presentation layer you should handle the option that the select will not be performed since the column names are invalid.
Have a look at UNPIVOT clause - I'm not sure it is applicable for your case but in some circumstances it can be used to query a value by the column name without dynamic SQL:
create table t1 (
a int,
b int,
c int
);
insert into t1 values
(1, 11, 111),
(2, 22, 222),
(3, 33, 333);
select a, col_name, col_value from t1
unpivot (col_value for col_name in (b, c)) as dt;
Result:
| a | col_name | col_value |
|---|----------|-----------|
| 1 | b | 11 |
| 1 | c | 111 |
| 2 | b | 22 |
| 2 | c | 222 |
| 3 | b | 33 |
| 3 | c | 333 |
(SQL Fiddle)
If you only need a value in a depending on some condition on (dynamically) either b or c, you can build the condition on that. If you need either values in column b or c, you can add ... WHERE col_name = ?. If you need more columns, you'd probably need to filter the column values on the un-pivoted table than pivot it again to get the values back in columns.

how to show remaining data by comparing another table

Table 1: ABC table 2: PQR
code|Name|Amount code|Name|Amount
----+----+----- ----+----+------
1 | A | 1000 1 | A | 1000
2 | B | 2000 2 | B | 2000
3 | C | 4000
4 | D | 1000
data in table 2 is insert from data based on table 1, now by pressing a button named "Remaining" i want to show data that is not present in table 2, to know which tuples i have missed to fill from table 1. How can i do it?
If the fields in the 2 tables are exactly the same and in the same order?
But you can't bother to put those fields in the SQL?
Then you could also use an EXCEPT
SELECT * FROM ABC
EXCEPT
SELECT * FROM PQR;
And if you're not certain that the fields are in the same order?
Then list them in the SQL.
That has also the benefit that the SQL will probably still work when one of the tables is altered.
SELECT [code], [Name], [Amount] from ABC
EXCEPT
SELECT [code], [Name], [Amount] FROM PQR;
It filters out the PQR records that are exactly the same as those found in ABC.
But normally, the methods that Tim Biegeleisen showed are more commonly used.
This answer assumes that you want to find all records in the first table which are not already present in the second table. One option uses a left join:
SELECT t1.*
FROM ABC t1
LEFT JOIN PQR t2
ON t1.code = t2.code AND t1.Name = t2.Name AND t1.Amount = t2.Amount
WHERE t2.code IS NULL;
We could also phrase this using EXISTS:
SELECT t1.*
FROM ABC t1
WHERE NOT EXISTS (SELECT 1 FROM PQR t2
WHERE t1.code = t2.code AND t1.Name = t2.Name AND
t1.Amount = t2.Amount);

Update table column based on comparison of columns from 2 table (SQL)

I have the following tables.
Table 1
Id | Values | Counts
1 | rock | 0
2 | tina | 0
3 | alex | 0
Table 2
Id | Values
1 | rock
2 | alex
3 | alex
4 | rock
5 | rock
6 | tina
As you can see, table 1 contains Values as rock, tina and alex. These column will always have unique values. Counts column should check the count of 'rock' in Table 2 and update it in Counts column. for e.g. rock is shown 3 times in table 2. The counts for rock should be then 3.
Similarly for other values. Can someone pls let me know how can i achieve this using SQL. Here is how the final table should look like.
Table 1
Id | Values | Counts
1 | rock | 3
2 | tina | 1
3 | alex | 2
Any help is appreciated. I searched online and couldnot find a possible solution for this scenario.
You can generally use a JOIN between 2 tables to update Table1 with values from Table2 (or further if you are using bridge tables).
UPDATE t1
SET t1.dataColumn = t2.dataColumn
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.keyColumn = t2.keyColumn
However, when you are using Aggregate functions (such as Count, Sum)you must utilize a subquery for the second table and perform the JOIN to that subquery
UPDATE t1
SET t1.Counts = sb.Counts
FROM Table1 AS t1
INNER JOIN (
SELECT [values], Counts = Count([values])
FROM Table2
GROUP BY [values]
) AS sb
ON t1.[values] = sb.[values]
Running this on your tables gave me this:
SELECT * FROM Table1
id values counts
---- ------- -------
1 rock 3
2 tina 1
3 alex 2
One thing concerning your table design; I generally recommend not using reserved/special/key words when naming tables, columns, or other database objects. I also try to avoid using the generic name id because it can get confusing when you start linking tables to one another, even idTable1 can make things a lot easier
In SQL Server, using a correlated subquery:
update t1
set t1.Counts = (
select count(*)
from t2
where t2.[Values] = t1.[Values]
);
rextester demo: http://rextester.com/SBYNB72372
In MySQL, using a correlated subquery:
update t1
set t1.Counts = (
select count(*)
from t2
where t2.`Values` = t1.`Values`
);
rextester demo: http://rextester.com/DDDC21719
Although this sort of thing might be better calculated in a view instead of stored in the t1 table.
In SQL Server:
create view dbo.t1_with_counts as
select t1.Id, t1.[Values], count(t2.[Values]) as Counts
from t1
left join t2
on t1.[Values] = t2.[Values]
group by t1.Id, t1.[Values]
go
select *
from dbo.t1_with_counts;
In MySQL:
create view t1_with_counts as
select t1.Id, t1.`Values`, count(t2.`Values`) as Counts
from t1
left join t2
on t1.`Values` = t2.`Values`
group by t1.Id, t1.`Values`;
select *
from t1_with_counts;
I would question the wisdom of keeping track of a count in a table like that. That leads to poor relational database structure and management. Instead, I suggest you remove the count column from Table 1. Then, whenever you need to see the counts you use a view:
SELECT t1.ID, t1.VALUES, COUNT(t2.ID) AS VALUE_COUNT
FROM TABLE1 t1 LEFT JOIN TABLE2 t2 ON t1.VALUES = t2.VALUES
This results in a dynamically updated view of your data instead of a static view that has the potential for going stale without your realizing it.

Correctly generating a unique invoice id

I've been asked to clean up someone else's controller code, which generates an invoice, and I've run into something I don't know how to fix. The code in question is as follows (this is using EF 6: Code First):
var invid = db.TransportJobInvoice.Where(c => c.CompanyId == CompanyId)
.Max(i => i.InvoiceId);
var invoiceId = invid == null ? 1 : (int)invid + 1;
The code is supposed to generate an invoiceId based on the company the invoice is being created for. So a small table of this might look as follows:
------------------------------
| Id | CompanyId | InvoiceId |
------------------------------
| 1 | 1 | 1 |
------------------------------
| 2 | 1 | 2 |
------------------------------
| 3 | 1 | 3 |
------------------------------
| 4 | 2 | 1 |
------------------------------
| 5 | 2 | 2 |
------------------------------
As you can see, the invoiceId would be generated based on the current number of invoices for the company in question. However, I think it's reasonable to suggest that two threads could execute the query before this line is evaluated:
var invoiceId = invid == null ? 1 : (int)invid + 1;
which would result in the same invoiceId being generated for two different invoices.
Is there a simple solution to this, possibly leveraging Entity Framework to do this automatically?
I suggest using the identity for the primary key, very important!
I would then add a column for "CustomerInvoiceID" and put a compound unique key on CustomerID and CustomerInvoiceID".
Then, create a stored procedure that will populate the field CustomerInvoiceID after it has been inserted, here is some pseudo code:
CREATE PROCEDURE usp_PopulateCustomerInvoiceID
#PrimaryKey INT, --this is your primary key identity column
#CustomerID INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cnt INT;
SELECT #CNT = COUNT(1)
FROM TBL
WHERE CustomerID = #CustomerID
AND PrimaryKeyColumn <= #PrimaryKey
UPDATE tbl
SET CustomerInvoiceID = #cnt + 1
WHERE PrimaryKeyColumn = #PrimaryKey
END
Two possibilities:
Server-side: Don't compute the max(ID)+1 on the client. Instead, as part of the INSERT statement, compute the max(ID)+1, via an INSERT..SELECT statement.
Client-side: Instead of an incrementing int, generate a GUID on the client, and use that as your InvoiceID.
A rather different approach would be to create a separate table with the NextId for each CustomerId. As new customers are added you would add a new row to this table. It has the advantage that the numbers assigned to invoices can remain unique even if you allow deleting invoices.
create procedure GetInvoiceIdForCustomer
#CustomerId as Int,
#InvoiceId as Int Output
as
begin
set nocount on
begin transaction
update CustomerInvoiceNumbers
set #InvoiceId = NextId, NextId += 1
where CustomerId = #CustomerId
if ##RowCount = 0
begin
set #InvoiceId = 1
insert into CustomerInvoiceNumbers ( CustomerId, NextId ) values ( #CustomerId, #InvoiceId + 1 )
end
commit transaction
end
end
If you use an Identity field in SQL Server, this will be handled automatically.
I don't know if you can make the invoice id auto generated unless it's beinng threated as a foreign key (which I think it isn't).
You problem with multiple threads could be solved using a lock statement.
lock (myLock)
{
var invid = db.TransportJobInvoice.Where(c => c.CompanyId == CompanyId)
.Max(i => i.InvoiceId);
var invoiceId = invid == null ? 1 : (int)invid + 1;
}
This will guarantee that only thread is executing these statements.
Be careful though, this could cause performance issues when those statements are executed alot in parallel and the query takes some significant time to execute.

Categories