I have DB migration where FormTemplateId - foreign key and I need to set default value as Id of the latest record in [dbo].[FormTemplates]:
migrationBuilder.AddColumn<int>(
name: "FormTemplateId",
table: "ContainerMasterTypes",
type: "int",
nullable: false,
defaultValueSql: "SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC");
and have error:
Subqueries are not allowed in this context. Only scalar expressions are allowed.'
What's wrong? How can I fix that error?
Entity Framework only allows you to do what you can do already in SQL Server. For example, with the following table setup:
create table dbo.FormTemplates (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
insert dbo.FormTemplates (Name) values (N'Foo'), (N'Bar'), (N'Baz');
create table dbo.ContainerMasterTypes (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
Attempting to modify the ContainerMasterTypes table to add the new column with a subquery in the default constraint as you are doing would fail...
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC);
... with the error message that you are seeing in .NET:
Msg 1046 Level 15 State 1 Line 3
Subqueries are not allowed in this context. Only scalar expressions are allowed.
To do this in SQL Server you would instead wrap the query in a Scalar User-Defined Function and reference that from the default constraint, i.e.:
create function dbo.LatestFormTemplateId()
returns int as
begin
return (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC)
end
go
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (dbo.LatestFormTemplateId());
Testing that with data...
insert dbo.ContainerMasterTypes (Name, FormTemplateId) values (N'Something', 1);
insert dbo.ContainerMasterTypes (Name) values (N'Else');
select * from dbo.FormTemplates;
select * from dbo.ContainerMasterTypes;
Would yield the results:
Id
Name
1
Foo
2
Bar
3
Baz
Id
Name
FormTemplateId
1
Something
1
2
Else
3
You need to do the same thing from Entity Framework as well, creating a Scalar UDF that wraps your query and then adding your new column with the default constraint referencing the UDF.
I have a table and I fill one of the columns with a trigger if it is null or empty. I want to delete the trigger and do its job in code.
Do I have to first insert and after update or is there a better way?
In .NET Framework, ORM is NHibernate
CREATE TABLE [dbo].[Table]
(
[Id] INT NOT NULL PRIMARY KEY,
[Col1] NVARCHAR(50) NOT NULL,
[Col2] NVARCHAR(50) NOT NULL,
[Code] NVARCHAR(100) NULL
);
CREATE TRIGGER Update_Table
ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
DECLARE #id INT
SELECT #id = Id
FROM inserted
UPDATE [dbo].[Table]
SET Code = 'CODE' + Id
FROM [dbo].[Table]
WHERE Id = #id AND Code IS NULL
END
I did this
Table entity = new Table() { Col1 = "aaa", Col2 = "bbb" };
entity = _repo.insert(entity);
entity.Code = "CODE" + entity.Id;
_repo.Update(entity);
sometimes i do not need update. Because users send this column value.
Table entity = new Table() { Col1 = "aaa", Col2 = "bbb", Code = "ccc" };
entity = _repo.insert(entity);
I tried insert then update. It is OK. Just seeking a better way.
I would simplify it by making CODE computed column, like this
CREATE TABLE [dbo].[Table]
(
[Id] INT NOT NULL PRIMARY KEY,
[Col1] NVARCHAR(50) NOT NULL,
[Col2] NVARCHAR(50) NOT NULL,
[Code] AS 'Code' + CAST(Id as NVARCHAR)
)
so, when inserting data, Code will be populated automatically
Notwithstanding Nino's answer, an interceptor is common way to achieve this.
Update:
It appears that event listeners are also an applicable technique too: https://stackoverflow.com/a/867356/1162077
You don't say how you're generating the entity id when it's not supplied by, so the event you intercept/handle will depend on how you're doing that.
I have a supertype table called Student, and its subtype table called OtherStudents. I am wondering how to write a stored procedure that will insert the foreign key value into OtherStudents table every time a new record with a student type Other is inserted into Student.
Student table:
CREATE TABLE [dbo].[Student]
(
[STUD_ID] [INT] IDENTITY(1000009,1) NOT NULL,
[STUD_NAM] [VARCHAR](30) NOT NULL,
[STUD_EMAIL] [VARCHAR](50) NULL,
[CAMP_NAM] [VARCHAR](50) NULL,
[CAMP_ZIP] [INT] NULL,
[STUD_TYP] [CHAR](5) NOT NULL,
CONSTRAINT [PK_Student]
PRIMARY KEY CLUSTERED ([STUD_ID] ASC)
)
GO
ALTER TABLE [dbo].[Student] WITH CHECK
ADD CONSTRAINT [FK_Student_Campus]
FOREIGN KEY([CAMP_NAM], [CAMP_ZIP]) REFERENCES [dbo].[Campus] ([CAMP_NAM], [CAMP_ZIP])
GO
ALTER TABLE [dbo].[Student] CHECK CONSTRAINT [FK_Student_Campus]
GO
`OtherStudents` table:
CREATE TABLE [dbo].[OtherStudents]
(
[OSTUD_ID] [INT] IDENTITY(1000009,1) NOT NULL,
[STUD_ST] [VARCHAR](30) NULL,
[STUD_APT] [VARCHAR](5) NULL,
[STUD_CITY] [CHAR](6) NULL,
[STUD_STATE] [CHAR](2) NULL,
[STUD_ZIP] [INT] NULL,
[RPT_ATTM] [VARBINARY](4000) NULL,
[RPT_EYESCR] [VARBINARY](4000) NULL,
[DATE_LASTPASS] [DATE] NULL,
[DATE_LASTVSP] [DATE] NULL,
[STUD_ID] [INT] NULL,
CONSTRAINT [PK_OtherStudents]
PRIMARY KEY CLUSTERED ([OSTUD_ID] ASC)
)
GO
ALTER TABLE [dbo].[OtherStudents] WITH CHECK
ADD CONSTRAINT [FK_OtherStudents_Student]
FOREIGN KEY([STUD_ID]) REFERENCES [dbo].[Student] ([STUD_ID])
GO
ALTER TABLE [dbo].[OtherStudents] CHECK CONSTRAINT [FK_OtherStudents_Student]
GO
I wrote two stored procedures:
ALTER PROCEDURE [dbo].[BusPassStudents_Insert]
(#STUD_ST VARCHAR(30),
#STUD_APT VARCHAR(5),
#STUD_CITY CHAR(6),
#STUD_STATE CHAR(2),
#STUD_ZIP INT,
#RPT_ATTM AS VARBINARY(4000) = NULL,
#RPT_EYESCR AS VARBINARY(4000) = NULL,
#DATE_LASTPASS DATE,
#DATE_LASTVSP AS DATE = NULL)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.OtherStudents (STUD_ST, STUD_CITY, STUD_STATE, STUD_ZIP, RPT_ATTM, RPT_EYESCR,DATE_LASTPASS, DATE_LASTVSP)
VALUES (#STUD_ST, #STUD_CITY, #STUD_STATE, #STUD_ZIP, #RPT_ATTM, #RPT_EYESCR, #DATE_LASTPASS, #DATE_LASTVSP)
END
ALTER PROCEDURE [dbo].[Stud_InsertNew]
(#STUD_NAM VARCHAR(30),
#STUD_EMAIL VARCHAR(50),
#CAMP_NAM VARCHAR(50),
#CAMP_ZIP INT,
#STUD_TYP CHAR(5))
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.Student(STUD_NAM, STUD_EMAIL, CAMP_NAM, CAMP_ZIP, STUD_TYP)
VALUES (#STUD_NAM, #STUD_EMAIL, #CAMP_NAM, #CAMP_ZIP, #STUD_TYP);
DECLARE #STUD_ID INT
SET #STUD_ID = SCOPE_IDENTITY()
SELECT #STUD_ID
WHERE #STUD_TYP = 'Other'
END
Here is my C# code I have:
int campzip = int.Parse(ddlCamp.SelectedValue.Trim());
int StudentZip = int.Parse(txtStdZip.Text);
DateTime DateLastPass = DateTime.ParseExact(txtDateLastPass.Text, "yyyy-MM-dd", null);
using (var connection = new System.Data.SqlClient.SqlConnection(Helper.CnnVal("cis-laredoConnectionString")))
{
connection.Open();
var cmd = new SqlCommand("dbo.Stud_InsertNew", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#STUD_NAM", txtStdName.Text);
cmd.Parameters.AddWithValue("#STUD_EMAIL", txtStdEmail.Text);
cmd.Parameters.AddWithValue("#CAMP_NAM", ddlCamp.SelectedItem.ToString());
cmd.Parameters.AddWithValue("#CAMP_ZIP", campzip);
cmd.Parameters.AddWithValue("#STUD_TYP", "Other");
int getStudID = (int) cmd.ExecuteScalar();
cmd.Dispose();
connection.Close();
connection.Open();
var cmd2 = new SqlCommand("BusPassStudents_Insert", connection);
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("#STUD_ID", getStudID);
cmd2.Parameters.AddWithValue("#STUD_ST", txtStdStreet.Text);
cmd2.Parameters.AddWithValue("#STUD_APT", txtStdApt.Text);
cmd2.Parameters.AddWithValue("#STUD_CITY", txtStdCity.Text);
cmd2.Parameters.AddWithValue("#STUD_STATE", txtStdState.Text);
cmd2.Parameters.AddWithValue("#STUD_ZIP", StudentZip);
cmd2.Parameters.AddWithValue("#RPT_ATTM", fuAttend.FileBytes);
cmd2.Parameters.Add("#DATE_LASTPASS", SqlDbType.Date).Value = DateLastPass;
cmd2.ExecuteNonQuery();
cmd2.Dispose();
connection.Close();
}
I am trying to use ExecuteScalar to retrieve the inserted STUD_ID in Student table and post it back into the OtherStudents table.
I am getting error:
System.InvalidCastException: 'Specified cast is not valid.'
Any help would be appreciated!
You have to select something from your stored procedure to get the scalar value in the C# which is you missed, so in your Stud_InsertNew you can select the STUD_ID and use ExecuteScalar() in C# code as:
int getStudID = (int)cmd.ExecuteScalar();
Changes in stored procedure:
ALTER PROCEDURE [dbo].[Stud_InsertNew]
(#STUD_NAM VARCHAR(30),
#STUD_EMAIL VARCHAR(50),
#CAMP_NAM VARCHAR(50),
#CAMP_ZIP INT,
#STUD_TYP CHAR(5))
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.Student(STUD_NAM, STUD_EMAIL, CAMP_NAM, CAMP_ZIP, STUD_TYP)
VALUES (#STUD_NAM, #STUD_EMAIL, #CAMP_NAM, #CAMP_ZIP, #STUD_TYP);
// Need this line which returns the latest inserted Student Id
SELECT TOP 1 [STUD_ID]
FROM dbo.Student
ORDER BY [STUD_ID] DESC
END
After a comment from Mitch Wheat and also found here, that the best way to use SCOPE_IDENTIY() if you want to get the latest inserted records primary key's value, so you just need to change the SELECT statement to this:
DECLARE #STUD_ID INT
SET #STUD_ID = SCOPE_IDENTITY()
SELECT #STUD_ID
This is how I'd like to approach this.
Convert two procedures into a single one by passing all the required parameters.
Make use of the "inserted" table data to get the information that was just Inserted into table A and store the reference in table B.
*(i) You may want to pass the rest of the parameters required to store the data in table B. I haven't included them in the stored procedure.
(ii) I'm trying to insert data into table B if only the check on birthday passes. In your case the predicate/condition will differ.
USE SOMEDB;
CREATE TABLE A
(Id Integer IDENTITY(1,1),
[Name] VARCHAR(20),
[Birthday] DATE
)
create table B
(
B_Id INTEGER IDENTITY(10001,1),
A_Id Integer NULL,
ColumnX VARCHAR(20),
ColumnY varchar(20))
CREATE PROCEDURE [uspInsertIndividualDetails]
(#name varchar(20),
#birthday date
)
AS
BEGIN
CREATE TABLE #tmpXYZ(iD INT,birthday date);
INSERT INTO A([Name],[Birthday])
OUTPUT inserted.Id,inserted.Birthday into #tmpXYZ
select #name, #birthday
IF exists (select 1 from #tmpXYZ where Birthday >'2018-01-01')
begin
INSERT INTO B(A_Id,ColumnX, ColumnY)
SELECT Id,'A Value', 'Some Other Value' FROM #tmpXYZ
end
END
execute [uspInsertIndividualDetails] #name ='John Doe', #birthday = '1972-10-01'
SELECT * FROM a
SELECT * FROM b
execute [uspInsertIndividualDetails] #name ='John DoeJr', #birthday = '2018-10-01'
SELECT * FROM a
SELECT * FROM b
Hope this helps.
You can do this without making database connection twice by making one stored procedure which combines the two stored procedure's input parameters as:
Reformed stored procedure:
ALTER PROCEDURE [dbo].[Stud_InsertNew]
(#STUD_NAM VARCHAR(30),
#STUD_EMAIL VARCHAR(50),
#CAMP_NAM VARCHAR(50),
#CAMP_ZIP INT,
#STUD_TYP CHAR(5),
// Parameters for other student
#STUD_APT VARCHAR(5),
#STUD_CITY CHAR(6),
#STUD_STATE CHAR(2),
#STUD_ZIP INT,
#RPT_ATTM AS VARBINARY(4000) = NULL,
#RPT_EYESCR AS VARBINARY(4000) = NULL,
#DATE_LASTPASS DATE,
#DATE_LASTVSP AS DATE = NULL)
AS
BEGIN
DECLARE #OTHERCONSTANT NVARCHAR(MAX)
SET #OTHERCONSTANT = 'Other' // You can set to a text that coming from C#
SET NOCOUNT ON
INSERT INTO dbo.Student(STUD_NAM, STUD_EMAIL, CAMP_NAM, CAMP_ZIP, STUD_TYP)
VALUES (#STUD_NAM, #STUD_EMAIL, #CAMP_NAM, #CAMP_ZIP, #STUD_TYP);
IF(#STUD_TYP = #OTHERCONSTANT)
BEGIN
// Need this line which returns the latest inserted Student Id
DECLARE #STUD_ID INT = NULL
SET #STUD_ID = SCOPE_IDENTITY()
INSERT INTO dbo.OtherStudents (STUD_ST, STUD_CITY, STUD_STATE, STUD_ZIP, RPT_ATTM, RPT_EYESCR,DATE_LASTPASS, DATE_LASTVSP)
VALUES (#STUD_ST, #STUD_CITY, #STUD_STATE, #STUD_ZIP, #RPT_ATTM, #RPT_EYESCR, #DATE_LASTPASS, #DATE_LASTVSP)
END
END
C# code:
using(var connection = new System.Data.SqlClient.SqlConnection(Helper.CnnVal("cis-laredoConnectionString"))) {
connection.Open();
var cmd = new SqlCommand("dbo.Stud_InsertNew", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#STUD_NAM", txtStdName.Text);
cmd.Parameters.AddWithValue("#STUD_EMAIL", txtStdEmail.Text);
cmd.Parameters.AddWithValue("#CAMP_NAM", ddlCamp.SelectedItem.ToString());
cmd.Parameters.AddWithValue("#CAMP_ZIP", campzip);
cmd.Parameters.AddWithValue("#STUD_TYP", "Other");
cmd.Parameters.AddWithValue("#STUD_ST", txtStdStreet.Text);
cmd.Parameters.AddWithValue("#STUD_APT", txtStdApt.Text);
cmd.Parameters.AddWithValue("#STUD_CITY", txtStdCity.Text);
cmd.Parameters.AddWithValue("#STUD_STATE", txtStdState.Text);
cmd.Parameters.AddWithValue("#STUD_ZIP", StudentZip);
cmd.Parameters.AddWithValue("#RPT_ATTM", fuAttend.FileBytes);
cmd.Parameters.Add("#DATE_LASTPASS", SqlDbType.Date).Value = DateLastPass;
cmd.ExecuteNonQuery();
cmd.Dispose();
if (connection.State == ConnectionState.Open) {
connection.Close();
}
}
PUTTING DATA ON 5TH TABLE FROM OTHER TABLES OR FROM USERS INPUT
STORED PROCEDURE FOR INSERTING DATA ON IT
I connected it in my windows form.
Table supplier
CREATE TABLE SUPPLIER
(
SUPPLIERID int identity(001,1) PRIMARY KEY NOT NULL,
SUPPLIERNAME varchar(50) NOT NULL,
ADDRESS varchar(50) NOT NULL,
Contact# varchar(50) NOT NULL
)
Table products:
CREATE TABLE PRODUCTS
(
PRODUCTID int identity(301, 1) PRIMARY KEY NOT NULL,
PNAME varchar(50) NOT NULL,
PCOLOR varchar(50)
FOREIGN KEY REFERENCES PRODUCTCOLOR(PRODUCTCOLOR) NOT NULL,
PPRICE INT NOT NULL
)
Table productcolor:
CREATE TABLE PRODUCTCOLOR
(
PRODUCTCOLOR varchar(50) PRIMARY KEY NOT NULL
)
Table customer:
CREATE TABLE CUSTOMER
(
CUSTOMERID int identity(601,1) PRIMARY KEY NOT NULL,
FIRSTNAME varchar(50) NOT NULL,
LASTNAME varchar(50) NOT NULL,
EMAIL varchar(50) NOT NULL ,
ADDRESS varchar(50) NOT NULL,
TELEPHONE varchar(50) NOT NULL,
username varchar(50),
password varchar (50)
)
I want to put(connect) them in the 5th table and create a stored procedure with it
Table orders:
CREATE TABLE ORDERS
(
Cusid int NOT NULL foreign key references CUSTOMER(CUSTOMERID),
Proid int NOT NULL foreign key references PRODUCTS(PRODUCTID),
Supp int NOT NULL foreign key references SUPPLIER(SUPPLIERID),
FNAME VARCHAR(50),
LNAME VARCHAR(50),
PRONAME VARCHAR(50),
SUPNAME VARCHAR(50),
PCOLOR varchar(50)FOREIGN KEY REFERENCES PRODUCTCOLOR(PRODUCTCOLOR)NOT NULL,
QUANTITY VARCHAR(50)primary key NOT NULL
)
Stored procedure:
CREATE PROCEDURE sp_order
#FNAME varchar(50),
#LNAME varchar(50),
#PRONAME varchar(50),
#SUPNAME varchar(50),
#PCOLOR varchar(50),
#QUANTITY int
AS
INSERT INTO ORDERS
VALUES (#FNAME, #LNAME, #PRONAME, #SUPNAME, #PCOLOR, #QUANTITY)
RETURN 0
I don't know if my 5th table is right and my stored procedure.
I've tried many things but it's kinda confusing
If you are not inserting into ALL columns of a table then the INSERT statement must list the specific columns:
INSERT INTO ORDERS (FNAME, LNAME, PRONAME, SUPNAME, PCOLOR, QUANTITY)
VALUES(#FNAME,#LNAME,#PRONAME , #SUPNAME, #PCOLOR, #QUANTITY);
Notice how the column names are specified and also the values are provided.
However, your table has defined NOT NULL columns so you must provide values for those.
Your procedure should look more like this:
CREATE PROCEDURE sp_order
#Cusid int,
#Proid int,
#Supp int,
#FNAME varchar(50),
#LNAME varchar(50),
#PRONAME varchar(50),
#SUPNAME varchar(50),
#PCOLOR varchar(50),
#QUANTITY int
AS
INSERT INTO ORDERS (Cusid, Proid, Supp, FNAME, LNAME, PRONAME, SUPNAME, PCOLOR, QUANTITY)
VALUES(#Cusid, #Proid, #Supp, #FNAME,#LNAME,#PRONAME , #SUPNAME, #PCOLOR, #QUANTITY);
RETURN 0;
Now you have the issue about obtaining the values for those foreign keys. I can't help with that based on the information you have provided.
as an aside you shouldn't name procedures with "sp_" because that is a special prefix that will hit you with a small performance penalty.
I have created a function in MSSQL 2005 and I want to run this function using linq. My function has 2 parameters. The definition is seen below:
USE [MobileGateway]
GO
/****** Object: UserDefinedFunction [dbo].[Fn_GetNoLocationAddress] Script Date: 11/07/2012 08:27:58 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Fn_GetNoLocationAddress]
(
-- Add the parameters for the function here
#Site nvarchar(255),
#ReceivedDate int
)
RETURNS #noLocationAddress TABLE (
RequestID int NOT NULL,
Barcode varchar(50) NOT NULL,
AdrID int NOT NULL,
Name varchar(50) NOT NULL,
Street varchar(50) NOT NULL,
HouseNo varchar(4) NOT NULL,
Postal varchar(8) NOT NULL,
City varchar(50) NOT NULL,
Country varchar(50) NOT NULL,
Latitude varchar(50) NOT NULL,
Longitude varchar(50) NOT NULL,
ReveivedDate datetime NOT NULL
)
AS
BEGIN
-- Add the SELECT statement with parameter references here
INSERT INTO #noLocationAddress
SELECT TOP (100) PERCENT Request1.RequestID, TrackIT.dbo.Sending.Barcode, TrackIT.dbo.Address_View.AdrID, TrackIT.dbo.Address_View.Name,
TrackIT.dbo.Address_View.Street, TrackIT.dbo.Address_View.HouseNo, TrackIT.dbo.Address_View.Postal, TrackIT.dbo.Address_View.City,
TrackIT.dbo.Address_View.Country, Request1.Latitude, Request1.Longitude, Request1.ReceivedDate
FROM (SELECT DISTINCT RequestID, LTRIM([Content]) AS Barcode, Latitude, Longitude, ReceivedDate
FROM dbo.RequestWithLocation
WHERE (Site LIKE #Site) AND ([Content] <> '') AND (AddressID = '0') AND (ReceivedDate > DATEADD(day, -#ReceivedDate, GETDATE()))) AS Request1 INNER JOIN
TrackIT.dbo.Sending ON Request1.Barcode = TrackIT.dbo.Sending.Barcode INNER JOIN
TrackIT.dbo.Address_View ON TrackIT.dbo.Sending.DeliveryAdrID = TrackIT.dbo.Address_View.AdrID
ORDER BY TrackIT.dbo.Address_View.AdrID
RETURN
END
As you can see I have 2 parameters and I'm returning a table with the information. But I need to use linq for executing the this function.
Can anyone help? thanks
I am assuming that you are using a dbml (linq to sql) file.
Drag drop your User defined function from the server explorer in visual studio into your dbml.
Then from the datacontext object of your dbml you can call the function directly.
For example if your dbml file name is xyz.dbml then your datacontext object's name would be 'XyzDataContext', unless you have changed it.
Then try this.
XyzDataContext db = new XyzDataContext();
List<Fn_GetNoLocationAddressResult> = db.Fn_GetNoLocationAddress("site", 25).ToList();