2

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!

  • 2
    Hi and welcome to SO. It is not clear to me what you are trying to accomplish. And images of tables are not helpful. Why? https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-on-so-when-asking-a-question/285557#285557 – Sean Lange Dec 02 '18 at 05:42
  • In `[Stud_InsertNew]` stored proc check for `@STUD_TYP= 'other'` then `INSERT INTO OtherStudents` – Prashant Pimpale Dec 02 '18 at 05:53
  • 1
    Thank you for the replies, I have updated the images to the DDL statements. Hope that would be better! –  Dec 02 '18 at 06:19
  • @NicoleWoo It would better if you explain your requirement well! Refer my previous comment, is that what you wanted? – Prashant Pimpale Dec 02 '18 at 06:23
  • 1
    @PrashantPimpale Sorry for the confusion, I am super new to Sql. What I am trying to do is each time a new record is added into Student table, the STUD_ID auto-generated will be retrieved and inserted in the foreign key column of the OtherStudents table. I did some research saying that OUTPUT inserted.STUD_ID might be helpful, but I am not sure how to do it. I am now trying the ExecuteScalar() in C#, but still getting errors saying 'the specified cast is not valid'. Hope these would help you understand my requirement! –  Dec 02 '18 at 06:34
  • In C#, have have you tried this--> *`int STUD_ID = (int)cmd.ExecuteScalar();`* – Prashant Pimpale Dec 02 '18 at 06:36
  • Yes, I just tried it. It returned me the same error. –  Dec 02 '18 at 06:41

3 Answers3

2

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
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84
  • It didn't give me error anymore. But it returns me 3 duplicated records in Student table each time I submit and no value returned to the OtherStudents table...did I mess something up? lol –  Dec 02 '18 at 07:01
  • 1
    I see...I forgot to change cmd to cmd2 in c# for the BusPassstudent insert. I will try it again. –  Dec 02 '18 at 07:03
  • 2
    this solution is flawed. Use scope_identity() or preferably OUTPUT clause to retrieve inserted Student Id – Mitch Wheat Dec 02 '18 at 07:41
  • @MitchWheat yes, exactly that's why posted another answer, Is that fine or still need to improve? – Prashant Pimpale Dec 02 '18 at 07:43
  • 1
    Thank you for the refined code. I have changed to the SCOPE_IDENTITY() and also added where STUD_TYP = "Other". –  Dec 02 '18 at 19:34
1

This is how I'd like to approach this.

  1. Convert two procedures into a single one by passing all the required parameters.

  2. 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.

Hanuma
  • 41
  • 4
0

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();
 }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84