I have two tables as follows:-
Table1:
-------------------------------------
PageID|Content|TitleID(FK)|LanguageID
-------------------------------------
1 |abc |101 |1
2 |xyz |102 |1
--------------------------------------
Table2:
-------------------------
TitleID|Title |LanguageID
-------------------------
101 |Title1|1
102 |Title2|1
------------------------
I don't want to add duplicates in my Table1 (content table). Like.. there can be no two pages with the same title. What check do I need to add in my Insert/Update Stored Procedure? How do I make sure duplicates are never added.
I have tried as follows:-
CREATE PROC InsertUpdatePageContent
(
#PageID int,
#Content nvarchar(2000),
#TitleID int
)
AS
BEGIN
IF(#PageID=-1)
BEGIN
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID = #LANGUAGEID))
BEGIN
INSERT INTO Table1(Content,TitleID)
VALUES(#Content,#TitleID)
END
END
ELSE
BEGIN
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID = #LANGUAGEID))
BEGIN
UPDATE Table1
SET Content = #Content, TitleID = #TitleID
WHERE PAGEID = #PAGEID
END
END
END
Now what is happening is that it is inserting new records alright and won't allow duplicates to be added but when I update its giving me problem.
On my aspx Page I have a drop down list control that is bound to DataSource that returns Table 2 (title table) and I have a text box in which user types Page's content to be stored.
When I update, like lets say I have a row in my Table 1 as shown above with PageID=1.
Now when I am updating this row, like I didn't change the Title from the drop down and only changed Content in the text box, its not updating the record ..and when Stored procedure's Update Query does not execute it displays a Label that says "Page with this title exists already."
So whenever I am updating an existing record that label is displayed on screen.How do I change that IF condition in my Update stored procedure??
EDIT:
#gbn: will that IF condition work in case of update? I mean lets say I am updating the Page with TitleID=1, I changed its content, then when I update, it's gonna execute that IF condition and it still won't update coz TitleID=1 already exits!It will only update if TitleID=1 is not there in Table1. Isn't it? Guess I am getting confused. Please answer. Thanks.
Applying a UNIQUE key constraint on TitleID in Table 1 would ensure that no duplicate values for TitleID get saved into the table.
And as far as your stored procedure's code is concerned, gbn's reply highlights the conditional check to be corrected to make it work.
For Update, you cna put this condition to avoid same titles for multiple pages..
IF NOT EXISTS (SELECT 1 from Table1 where TitleId = #TitleID and PageID <> #PageID) -- This makes sure that there is no 'other page' with same title (updated from UI)
{
UPDATE Table1
SET Content = #Content, TitleID = #TitleID
WHERE PAGEID = #PAGEID
}
This will always return false
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID=LANGUAGEID))
You are comparing a column with itself, it matches = EXISTS
It should be something like
CREATE PROC InsertUpdatePageContent (
#PageID int,
#Content nvarchar(2000),
#TitleID int
)
AS
IF(#PageID=-1)
BEGIN
IF NOT EXISTS (SELECT TitleID FROM Table1 WHERE TitleID=#TitleID)
INSERT INTO Table1(Content,TitleID) VALUES(#Content,#TitleID)
END
ELSE
UPDATE Table1 SET Content= #Content, TitleID=#TitleID WHERE PAGEID=#PAGEID
GO
To protect your data, you should have unique index on TitleID in Table1.
I am not sure I completely understand, but I'll take a shot at it for you. I will assume you can't change your data structure.
First I would put a unique constraint on the Title column in the Table2. Then I would put a unique constraint on the TitleId (FK) in the Table1. This will ensure that you don't have two entries in Table1 with the same title.
Then in the stored procedure I would do the comparison against the parameters being brought in and what exists in the database so that either update or insert is being validated against what's being passed in.
I normally use stored procedures as an API between the relational data model and the object model for some software. I rarely expose database identity columns to stored procedure signatures for applications. Database identity columns get treated differently than unique identifiers that software might use to identify entities. It's hard to help much more without additional information about the rest of the application and other stored procedures that need to work together for your solution.
Related
How am I supposed to get the IDENTITY of an inserted row?
I know about ##IDENTITY and IDENT_CURRENT and SCOPE_IDENTITY, but don't understand the implications or impacts attached to each.
Can someone please explain the differences and when I would be using each?
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes. You need to be careful here, since it's across scopes. You could get a value from a trigger, instead of your current statement.
SCOPE_IDENTITY() returns the last identity value generated for any table in the current session and the current scope. Generally what you want to use.
IDENT_CURRENT('tableName') returns the last identity value generated for a specific table in any session and any scope. This lets you specify which table you want the value from, in case the two above aren't quite what you need (very rare). Also, as #Guy Starbuck mentioned, "You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into."
The OUTPUT clause of the INSERT statement will let you access every row that was inserted via that statement. Since it's scoped to the specific statement, it's more straightforward than the other functions above. However, it's a little more verbose (you'll need to insert into a table variable/temp table and then query that) and it gives results even in an error scenario where the statement is rolled back. That said, if your query uses a parallel execution plan, this is the only guaranteed method for getting the identity (short of turning off parallelism). However, it is executed before triggers and cannot be used to return trigger-generated values.
I believe the safest and most accurate method of retrieving the inserted id would be using the output clause.
for example (taken from the following MSDN article)
USE AdventureWorks2008R2;
GO
DECLARE #MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM #MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
I'm saying the same thing as the other guys, so everyone's correct, I'm just trying to make it more clear.
##IDENTITY returns the id of the last thing that was inserted by your client's connection to the database.
Most of the time this works fine, but sometimes a trigger will go and insert a new row that you don't know about, and you'll get the ID from this new row, instead of the one you want
SCOPE_IDENTITY() solves this problem. It returns the id of the last thing that you inserted in the SQL code you sent to the database. If triggers go and create extra rows, they won't cause the wrong value to get returned. Hooray
IDENT_CURRENT returns the last ID that was inserted by anyone. If some other app happens to insert another row at an unforunate time, you'll get the ID of that row instead of your one.
If you want to play it safe, always use SCOPE_IDENTITY(). If you stick with ##IDENTITY and someone decides to add a trigger later on, all your code will break.
The best (read: safest) way to get the identity of a newly-inserted row is by using the output clause:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare #IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into #IdentityOutput
values
( ... )
select #IdentityValue = (select ID from #IdentityOutput)
Add
SELECT CAST(scope_identity() AS int);
to the end of your insert sql statement, then
NewId = command.ExecuteScalar()
will retrieve it.
From MSDN
##IDENTITY, SCOPE_IDENTITY, and IDENT_CURRENT are similar functions in that they return the last value inserted into the IDENTITY column of a table.
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope. For more information, see IDENT_CURRENT.
IDENT_CURRENT is a function which takes a table as a argument.
##IDENTITY may return confusing result when you have an trigger on the table
SCOPE_IDENTITY is your hero most of the time.
When you use Entity Framework, it internally uses the OUTPUT technique to return the newly inserted ID value
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, joined back to the table, and return the row value out of the table.
Note: I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
This technique (OUTPUT) is only available on SQL Server 2008 or newer.
Edit - The reason for the join
The reason that Entity Framework joins back to the original table, rather than simply use the OUTPUT values is because EF also uses this technique to get the rowversion of a newly inserted row.
You can use optimistic concurrency in your entity framework models by using the Timestamp attribute: 🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
When you do this, Entity Framework will need the rowversion of the newly inserted row:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
And in order to retrieve this Timetsamp you cannot use an OUTPUT clause.
That's because if there's a trigger on the table, any Timestamp you OUTPUT will be wrong:
Initial insert. Timestamp: 1
OUTPUT clause outputs timestamp: 1
trigger modifies row. Timestamp: 2
The returned timestamp will never be correct if you have a trigger on the table. So you must use a separate SELECT.
And even if you were willing to suffer the incorrect rowversion, the other reason to perform a separate SELECT is that you cannot OUTPUT a rowversion into a table variable:
DECLARE #generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO #generated_keys
VALUES('Malleable logarithmic casing');
The third reason to do it is for symmetry. When performing an UPDATE on a table with a trigger, you cannot use an OUTPUT clause. Trying do UPDATE with an OUTPUT is not supported, and will give an error:
Cannot use UPDATE with OUTPUT clause when a trigger is on the table
The only way to do it is with a follow-up SELECT statement:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE ##ROWCOUNT > 0 AND TurboEncabulatorID = 1
I can't speak to other versions of SQL Server, but in 2012, outputting directly works just fine. You don't need to bother with a temporary table.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
By the way, this technique also works when inserting multiple rows.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
Output
ID
2
3
4
##IDENTITY is the last identity inserted using the current SQL Connection. This is a good value to return from an insert stored procedure, where you just need the identity inserted for your new record, and don't care if more rows were added afterward.
SCOPE_IDENTITY is the last identity inserted using the current SQL Connection, and in the current scope -- that is, if there was a second IDENTITY inserted based on a trigger after your insert, it would not be reflected in SCOPE_IDENTITY, only the insert you performed. Frankly, I have never had a reason to use this.
IDENT_CURRENT(tablename) is the last identity inserted regardless of connection or scope. You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into.
ALWAYS use scope_identity(), there's NEVER a need for anything else.
One other way to guarantee the identity of the rows you insert is to specify the identity values and use the SET IDENTITY_INSERT ON and then OFF. This guarantees you know exactly what the identity values are! As long as the values are not in use then you can insert these values into the identity column.
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
This can be a very useful technique if you are loading data from another source or merging data from two databases etc.
Create a uuid and also insert it to a column. Then you can easily identify your row with the uuid. Thats the only 100% working solution you can implement. All the other solutions are too complicated or are not working in same edge cases.
E.g.:
1) Create row
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2) Get created row
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
Even though this is an older thread, there is a newer way to do this which avoids some of the pitfalls of the IDENTITY column in older versions of SQL Server, like gaps in the identity values after server reboots. Sequences are available in SQL Server 2016 and forward which is the newer way is to create a SEQUENCE object using TSQL. This allows you create your own numeric sequence object in SQL Server and control how it increments.
Here is an example:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
Then in TSQL you would do the following to get the next sequence ID:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
Here are the links to CREATE SEQUENCE and NEXT VALUE FOR
Complete solution in SQL and ADO.NET
const string sql = "INSERT INTO [Table1] (...) OUTPUT INSERTED.Id VALUES (...)";
using var command = connection.CreateCommand();
command.CommandText = sql;
var outputIdParameter = new SqlParameter("#Id", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(outputIdParameter);
await connection.OpenAsync();
var outputId= await command.ExecuteScalarAsync();
await connection.CloseAsync();
int id = Convert.ToInt32(outputId);
After Your Insert Statement you need to add this. And Make sure about the table name where data is inserting.You will get current row no where row affected just now by your insert statement.
IDENT_CURRENT('tableName')
My database contains three tables called Object_Table, Data_Table and Link_Table. The link table just contains two columns, the identity of an object record and an identity of a data record.
I want to copy the data from DATA_TABLE where it is linked to one given object identity and insert corresponding records into Data_Table and Link_Table for a different given object identity.
I can do this by selecting into a table variable and the looping through doing two inserts for each iteration.
Is this the best way to do it?
Edit : I want to avoid a loop for two reason, the first is that I'm lazy and a loop/temp table requires more code, more code means more places to make a mistake and the second reason is a concern about performance.
I can copy all the data in one insert but how do get the link table to link to the new data records where each record has a new id?
In one statement: No.
In one transaction: Yes
BEGIN TRANSACTION
DECLARE #DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT #DataID = scope_identity();
INSERT INTO LinkTable VALUES (#ObjectID, #DataID);
COMMIT
The good news is that the above code is also guaranteed to be atomic, and can be sent to the server from a client application with one sql string in a single function call as if it were one statement. You could also apply a trigger to one table to get the effect of a single insert. However, it's ultimately still two statements and you probably don't want to run the trigger for every insert.
You still need two INSERT statements, but it sounds like you want to get the IDENTITY from the first insert and use it in the second, in which case, you might want to look into OUTPUT or OUTPUT INTO: http://msdn.microsoft.com/en-us/library/ms177564.aspx
The following sets up the situation I had, using table variables.
DECLARE #Object_Table TABLE
(
Id INT NOT NULL PRIMARY KEY
)
DECLARE #Link_Table TABLE
(
ObjectId INT NOT NULL,
DataId INT NOT NULL
)
DECLARE #Data_Table TABLE
(
Id INT NOT NULL Identity(1,1),
Data VARCHAR(50) NOT NULL
)
-- create two objects '1' and '2'
INSERT INTO #Object_Table (Id) VALUES (1)
INSERT INTO #Object_Table (Id) VALUES (2)
-- create some data
INSERT INTO #Data_Table (Data) VALUES ('Data One')
INSERT INTO #Data_Table (Data) VALUES ('Data Two')
-- link all data to first object
INSERT INTO #Link_Table (ObjectId, DataId)
SELECT Objects.Id, Data.Id
FROM #Object_Table AS Objects, #Data_Table AS Data
WHERE Objects.Id = 1
Thanks to another answer that pointed me towards the OUTPUT clause I can demonstrate a solution:
-- now I want to copy the data from from object 1 to object 2 without looping
INSERT INTO #Data_Table (Data)
OUTPUT 2, INSERTED.Id INTO #Link_Table (ObjectId, DataId)
SELECT Data.Data
FROM #Data_Table AS Data INNER JOIN #Link_Table AS Link ON Data.Id = Link.DataId
INNER JOIN #Object_Table AS Objects ON Link.ObjectId = Objects.Id
WHERE Objects.Id = 1
It turns out however that it is not that simple in real life because of the following error
the OUTPUT INTO clause cannot be on
either side of a (primary key, foreign
key) relationship
I can still OUTPUT INTO a temp table and then finish with normal insert. So I can avoid my loop but I cannot avoid the temp table.
I want to stress on using
SET XACT_ABORT ON;
for the MSSQL transaction with multiple sql statements.
See: https://msdn.microsoft.com/en-us/library/ms188792.aspx
They provide a very good example.
So, the final code should look like the following:
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE #DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT #DataID = scope_identity();
INSERT INTO LinkTable VALUES (#ObjectID, #DataID);
COMMIT
It sounds like the Link table captures the many:many relationship between the Object table and Data table.
My suggestion is to use a stored procedure to manage the transactions. When you want to insert to the Object or Data table perform your inserts, get the new IDs and insert them to the Link table.
This allows all of your logic to remain encapsulated in one easy to call sproc.
If you want the actions to be more or less atomic, I would make sure to wrap them in a transaction. That way you can be sure both happened or both didn't happen as needed.
You might create a View selecting the column names required by your insert statement, add an INSTEAD OF INSERT Trigger, and insert into this view.
Before being able to do a multitable insert in Oracle, you could use a trick involving an insert into a view that had an INSTEAD OF trigger defined on it to perform the inserts. Can this be done in SQL Server?
Insert can only operate on one table at a time. Multiple Inserts have to have multiple statements.
I don't know that you need to do the looping through a table variable - can't you just use a mass insert into one table, then the mass insert into the other?
By the way - I am guessing you mean copy the data from Object_Table; otherwise the question does not make sense.
//if you want to insert the same as first table
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = #mysql_query($qry);
$qry2 = "INSERT INTO table2 (one,two, three) VVALUES('$one','$two','$three')";
$result = #mysql_query($qry2);
//or if you want to insert certain parts of table one
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = #mysql_query($qry);
$qry2 = "INSERT INTO table2 (two) VALUES('$two')";
$result = #mysql_query($qry2);
//i know it looks too good to be right, but it works and you can keep adding query's just change the
"$qry"-number and number in #mysql_query($qry"")
I have 17 tables this has worked in.
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters
-- command (Ctrl-Shift-M) to fill in the parameter
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE InsetIntoTwoTable
(
#name nvarchar(50),
#Email nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
insert into dbo.info(name) values (#name)
insert into dbo.login(Email) values (#Email)
END
GO
I don't know is it possible or not.
I have a table that keeps records for a book issue return.There are two columns in this one is [status] and other [bookid] .I want to add a constraint in sql that will restrict user to insert duplicate record for status="issue" with same bookid.
For example if there is already a record with status='issue' and bookid=1 then it must not allow to insert other record with status="issue" and bookid=1 but there can me multipule records with other status like staus='return' and bookid=1 may occur a number of times.
Or there may be solution using linq to sql in c#
You do not need a user defined function, in general. In SQL Server (and many other databases) you can just use a filtered index:
create unique index unq_bookissue
where status = 'issued' ;
In earlier versions of SQL Server you can do this with a computed column, assuming that you have a table with columns such as:
BookId, which is repeated across rows.
Status, which should be unique when the value is issue.
BookIssueId, which uniquely identifies each row.
Then, add a computed column to keep track of status = 'issue':
alter table t add computed_bookissueid as (case when status = 'issue' then -1 else BookIssueId end);
Now add a unique index on this column and BookId:
create unique index unq_bookid_issue on (BookId, computed_bookissueid);
You have a complex condition here, so a UNIQUEconstraint won't help you. You will need a CHECKconstraint.
You first need a function to to do your check:
CREATE FUNCTION dbo.IsReturnDuplicate
(
#id INT,
#bookid INT,
#status VARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
RETURN (SELECT TOP 1 COUNT (*) FROM bookreturns WHERE (id <> #id) AND (status = #status) AND (bookid = #bookid) AND (status = 'issue')
END
This will return 1 if there is already a row in the table that has status 'issue' and has a different id
You can then create a CHECK constraint using this function
CREATE TABLE bookreturns (
--...
CONSTRAINT CK_bookreturns_status CHECK (dbo.IsReturnDuplicate(id, bookid, status) == 0)
)
Using Gordon answer
create unique index unq_bookissuedReference
on Books(Book_id) where [status] = 'issued'
Works for me
Forgive me as I'm new to databases. I have a recipe form that the user can enter several ingredients and the recipe name into. With this form I have the following tables: Recipe{recipeID, recipeName} RecipeIngredient{recipeID,ingredientID,measurement} Ingredient{ingredientID, ingredientName}
RecipeIngredients acts as an intermediate table for the one to many relationship (one recipe to many ingredients). I assigned recipeID as a foreign key inside that table to the primary key recipeID in the recipe table. I thought a foreign key would update based on the primary key updating, but if I insert into the Recipe table the RecipeIngredients table does nothing.
The recipe and the ingredients are assigned ID's automatically.I'm trying to link the ID of one recipe to the auto assigned ID's of each ingredient. For example if the user enters in 8 ingredients, a unique ID is assigned to each of those ingredients. I need the RecipeIngredients table to duplicate those ID's and associate them with the auto-assigned Recipe ID.
I've tried several solutions for updating a column based on changes in other tables. I've tried modifying this MySQL update statement and also modifying this SQL server but I can't seem to get either of them to work.
Suggestions?
In general, you will need to get the new recipeID from the first insert, then use that when you insert into the ingredients table.
Do you have access to the newly inserted recipeID from the insert into the recipe table? How are you doing this insert?
Here is an example Stored Procedure which goes into the database to do the insert / update and return the id:
CREATE PROCEDURE [dbo].[spAlertCategoryInsUpd]
#AlertCategoryID int,
#AlertID int,
#CategoryID int,
#ScreenSortOrder int
AS
SET NOCOUNT ON
IF EXISTS(SELECT [AlertCategoryID] FROM [dbo].[AlertCategories] WHERE [AlertCategoryID] = #AlertCategoryID)
BEGIN
UPDATE [dbo].[AlertCategories] SET
[AlertID] = #AlertID,
[CategoryID] = #CategoryID,
[ScreenSortOrder] = #ScreenSortOrder
WHERE
[AlertCategoryID] = #AlertCategoryID
SELECT #AlertCategoryID as AlertCategoryID;
END
ELSE
BEGIN
INSERT INTO [dbo].[AlertCategories] (
[AlertID],
[CategoryID],
[ScreenSortOrder]
)
OUTPUT INSERTED.AlertCategoryID AS AlertCategoryID
VALUES (
#AlertID,
#CategoryID,
#ScreenSortOrder
)
;
END
When trying to add a view to the edmx file, nothing happens.
I opened the edmx file using wxl editor and I noticed the following error:
warning 6013: The table/view
'CellularOrders.dbo.V_LINK' does not
have a primary key defined and no
valid primary key could be inferred.
This table/view has been excluded. To
use the entity, you will need to
review your schema, add the correct
keys, and uncomment it.
(importent thing - I didn't and don't need to add the table which the view based on to the edmx. Moreover, the view is only for doing select statements on the data)
So in the db, I updated the T_LINK table and made one of the fields that reflects on the view as primary key. And then, when I tryed again to add the view to the edmx nothing happens again.
How can I solve this??
Is there an option to fix this without doing anything to the table?
Can I add another view that will somehow wrap the old view but with fixed properties?
Just add a column to your view I added a Row_Number to create a key like this
SELECT ISNULL(CAST((row_number() OVER (ORDER BY tab.ENTRYDATE)) AS int), 0)
AS EDMXID,...other columns go on
the tab expression is table alias and the entrydate is just a field needed for row_number built in sql-server func.
you may choose diffrent ways, e.g.
select newid() as MYEDMXID,....so on
Hope Helps
Each table or view added to entity model must have some key. It actually doesn't have to be primary key. If the table doesn't have the primary key defined EF will try to infer a key using simple rule: It will take all non-nullable non-computed non-binary columns and marks them as an entity key. If none such column exist the entity cannot be automatically added and the designer will throw the mentioned warning. Workaround is adding the view manually and selecting the key yourselves but once you do it you cannot use Update from database because it will always overwrite your changes.
Your defined key should be unique otherwise you can have some other problems related to identity map used internally.
You can easily solve this problem by joining your view with any arbitrary table with a primary column. Just make sure that you only grab a single row from the table.
Here is an example:
CREATE VIEW dbo.myView
AS
SELECT
-- This column enables EF-import via designer by enabling PK generation
Id,
-- These columns belong to the view
[Count],
[Sum]
FROM
(
SELECT
COUNT(*) AS [Count]
,SUM(1) AS [Sum]
FROM
dbo.myTable
) TheViewItself
-- Grab a primary key of a single row from atable
INNER JOIN (SELECT TOP 1 Id FROM dbo.TableWithPrimaryKey) Id ON 1 = 1
"ON 1 = 1" join predicate looks odd. But I needed this to convince EF to import the view.
You can set one of your view columns as not Nullable by using "ISNULL" function as following
ALTER VIEW [dbo].[MyView]
AS
SELECT
ISNULL([StringID],'') AS [Id],
[Name]
FROM [Table]
GO
Use a new table only for link with your views, if your have more then 100k rows, EF6 not its better solution ;)
CREATE TABLE dbo.TablePrimate(Id int CONSTRAINT PK_TablePrimate PRIMARY KEY (Id))
go
set nocount on;
DECLARE #i int;
set #i=1
WHILE #i<10000
BEGIN
INSERT dbo.TablePrimate(Id) values(#i)
SET #i = #i + 1
END
--In fews seconds & 1 MB of storage
GO
Now joins with "MyView"
CREATE VIEW dbo.vwTickets
AS
SELECT TP.Id, MyPKView.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY Ticket) Line, MyView.*
FROM (
select Grupo, App, Ticket, Titulo, FApertura, Estado, Tipo from dbo.vwEvolutivos
union
select Grupo, App, Ticket, Titulo, FApertura, Estado, Tipo from dbo.vwIncidencias
) MyView
) MyPKView
JOIN dbo.TablePrimate TP ON TP.Id = Line
You can create a view in your database and make queries like this in your code:
List<users> _users = _context.users.SqlQuery("SELECT * FROM users_v").ToList<users>();
I tried to add PostgreSQl views for a long time but without success.