Linq DELETE generates UPDATE query in SQL server - c#

I have database model:
CONSTRAINT [FK_applications_orders] FOREIGN KEY ([order_id]) REFERENCES [dbo].[orders] ([order_id])
And controller action:
using (var tx = Database.Database.BeginTransaction())
{
var order = Database.Set<Order>().Find(someID);
var apps = Database.Set<Applications>().Where(x => x.Order.Id == order.Id).ToList();
Database.Delete(order);
tx.Commit();
}
I open SQL profiler to inspect what this line var apps = Database... generates, and look at this:
exec sp_executesql N'UPDATE [dbo].[Applications]
SET [order_id] = NULL
WHERE (([application_id] = #0) AND ([order_id] = #1))
',N'#0 uniqueidentifier,#1 int',#0=SomeId,#1=SomeOtherId
So why does the Delete call generate an UPDATE query in SQL server?

You have a FK constraint between Orders and Applications.
When you delete from the Orders table, EF will be doing an update on the Applicatoins table to enforce this constraint.
e.g. you have the following tables
Orders
order_id
1
2
Applications
application_id | order_id
100 | 1
101 | 2
When you delete an order (say order_id 1) if EF didn't do an update you would end up with
Orders
order_id
2
Applications
application_id | order_id
100 | 1 <---- What is this now ???
101 | 2
So it is updating that field to set it to null.
Applications
application_id | order_id
100 | null
101 | 2

Related

Insert into Oracle SQL or Update if value already exist?

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.

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.

Stored Procedure for date ranges in a single column

Finding a solution to an issue in my project
I have stages associated with contracts. That is, a contract can be in either Active stage, Process stage or Terminated stage.
I need to get the no the days the contract was in each stage.
For example, if a contract C1 was in Active stage from 20/10/2013 to 22/10/2013, then in the Process stage from 22/10/2013 to 25/10/2013 and finally in Terminated stage from 25/10/2013 to 26/10/2013 and then again in Active from 26/10/2013 to 28/10/2013, then I should get as result
Active = 4days
Process = 3days
Terminated = 1day /likewise something
My table is created with these columns:
EntryId (primary key)
StageId (foreign key to Stage table)
ContractId (foreign key to contract table)
DateofStageChange
How to do this in SQL Server?
As asked pls find the table entries:
EntryID | Stage ID | Contract ID | DateChange
1 | A1 | C1 |20/10/2013
2 | P1 | C1 |22/10/2013
3 | T1 | C1 |25/10/2013
4 | A1 | C1 |26/10/2013
5 | P1 | C1 |28/10/2013
6 | T1 | C1 |Null(currently in this stage)
Need to use group by on Stage ID
it is important to check and make sure how data is populated in your table.Based on just your sample data and also note that if your entryid is not in sequence then you can create one sequence using row_number.
declare #t table(EntryId int identity(1,1), StageId int,ContractId varchar(10),DateofStageChange date)
insert into #t values
(1,'C1','2013-10-20'),(1,'C1','2013-10-22'),(2,'C1','2013-10-22'),(2,'C1','2013-10-25')
,(3,'C1','2013-10-25'),(3,'C1','2013-10-26'),(1,'C1','2013-10-26'),(1,'C1','2013-10-28')
Select StageId,sum([noOfDays]) [totalNofDays] from
(select a.StageId,a.ContractId,a.DateofStageChange [Fromdate],b.DateofStageChange [ToDate]
,datediff(day,a.DateofStageChange,b.DateofStageChange) [noOfDays]
from #t a
inner join #t b on a.StageId=b.StageId and b.EntryId-a.EntryId=1)t4
group by StageId
You can't with your current structure.
You can get the latest one by doing datediff(d, getdate(), DateOfStageChange)
but you don't have any history so you can't get previous status
This can be done in SQL with CTE.
You didnt provide your tablenames, so you'll need to change where I've indicated below, but it would look like this:
;WITH cte
AS (
SELECT
DateofStageChange, StageID, ContractID,
ROW_NUMBER() OVER (ORDER BY ContractID, StageId, DateofStageChange) AS RowNum
FROM
DateOfStageChangeTable //<==== Change this table name
)
SELECT
a.ContractId,
a.StageId,
Coalesce(sum(DATEDIFF(d ,b.DateofStageChange,a.DateofStageChange)), 'CurrentState`) as Days
FROM
cte AS A
LEFT OUTER JOIN
cte AS B
ON A.RowNum = B.RowNum + 1 and a.StageId = b.StageId and a.ContractId = b.ContractId
group by a.StageId, a.ContractId
This really is just a self join that creates a row number on a table, orders the table by StageID and date and then joins to itself. The first date on the first row of the stage id and date, joins to the second date on the second row, then the daterange is calculated in days.
This assumes that you only have 2 dates for each stage, if you have several, you would just need to do a min and max on the cte table.
EDIT:
Based on your sample data, the above query should work well. Let me know if you get any syntax errors and I'll fix them.
I added a coalesce to indicate the state they are currently in.

select two columns from two different tables in the same db using mysql + use the output of query in C#

Dear Friends,
i want to select two columns from two different tables in the same db using mysql and set the output of the query to a variable in c#.
currently my code is as shown below:
MySqlCommand logcmdCheck = new MySqlCommand(query, connectionCheck);
string query = "SELECT DB.table1.column1,DB.table1.column2,DB.table2.column1,DB.table2.column2,DB.table2.column3 FROM DB.table1 WHERE DB.table1.column1=?x,DB.table2 WHERE DB.table2.column1=?y";
logcmdCheck.Parameters.AddWithValue("?x",UserName);
logcmdCheck.Parameters.AddWithValue("?y",emailID);
MySqlDataReader ldr = logcmdCheck.ExecuteReader();
A = ldr[0].ToString();
B = ldr[1].ToString();
C = ldr[2].ToString();
D = ldr[3].ToString();
E = ldr[4].ToString();
Error: Mysql query syntax is wrong.
Kindly please help me out with the mysql command to perform the requirement.
Thanks in advance
Suraj
You're going to have to use a SQL Join. Check it out here http://www.w3schools.com/sql/sql_join.asp. You need to have a foreign key in one of the tables that allows you to connect to the primary key of the other table. Every good database should be set up with tables that have foreign keys.
For example:
Table 1:
OrderNumber Name Order Total
1 John Smith 10.00
2 Sally Smith 5.00
3 Berry Jones 25.00
Table 2:
Item Number ItemTotal OrderNumber
1 5.00 1
2 5.00 1
3 2.50 2
4 2.50 2
5 25.00 3
In table 2 the OrderNumber is the foreign key that is able to join to table one. So your syntax would be:
SELECT * FROM table1 JOIN table2 ON table2.OrderNumber = table1.OrderNumber
That will give you one table which you can read from.

Need help with an update SP and renaming files in the directory

There's a table Commodity in my Database and there's a separate table CommodityImages. There's field CommodityCaption in the Commodity table. I am saving images in the CommodityImages table using the value in this field as follows:
Commodity Table:-
CommodityID | CommodityName | CommodityCaption | IsActive
__________________________________________________________
1 | NameA |NameA-CaptionA |True
2 | NameB |NameB-CaptionB |True
CommodityImages Table looks like :
CommodityImageID | CommodityID | CommodityImageName |
1 | 1 |NameA-CaptionA_100X200.bmp
2 | 1 |NameA-CaptionA_300X500.bmp
3 | 2 |NameB-CaptionB_100X200.bmp
4 | 2 |NameB-CaptionB_300X500.bmp
Now when the Caption field is updated in the Commodity table , say, caption for ID=1 is changed from NameA-CaptionA to NameAAA-CaptionAXYZ, I want the CommodityIamges Table to be updated as well.
Like I want the Image names against ID=1 to be updated too. Now when the caption has been changed to NameAAA-CaptionAXYZ, the image names in the second table should become:
NameAAA-CaptionAXYZ_100X200.bmp and NameAAA-CaptionAXYZ_300X500.bmp
How do I do this in my Update SP which is being used to update Commodity Table?
Also, I need to rename the image files in my directory to the latest Commodity Caption names. I know about File.Move(oldFileName, newFileName); but how will I search for these image files in my directory and rename them? All I have ever done is save files.
How to search and then rename?
Update SP :-
-- Description: INSERT PROCEDURE TO INSERT & UPDATE IN Commodity TABLE
-- =============================================
ALTER PROC [SAN].[InsertUpdateCommodity]
(
#CommodityID BIGINT,
#CommodityName nvarchar(100),
#CommodityCaption nvarchar(100),
#Active BIT
)
AS
BEGIN
IF(#CommodityID =-1)
BEGIN
INSERT INTO SAN.Commodity
(
CommodityName,
CommodityCaption,
Active
)
VALUES
(
#CommodityName,
#CommodityCaption,
#Active
)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE SAN.Commodity SET
CommodityName=#CommodityName,
CommodityCaption=#CommodityCaption,
Active=#Active
WHERE CommodityID=#CommodityID
SELECT #CommodityID
END
END
Here is a simplistic pseudocode:
void UpdateCaption(int Id, string oldName, string newName) {
db.StartTran();
db.Exec(string.format("update Commodity set CommodityCaption = '{0}'
where commodityId = {1}", oldName, Id));
List<CommodityImage> images = CommodityImages.Where(ci => ci.CommodityID = Id);
foreach (var img in images)
img.CommodityImageName = img.CommodityImageName.Replace(oldName, newName);
db.SubmitChanges();
db.CommitTran();
}

Categories