0

I am tasked with writing a stored procedure that will validate the input data against a few tables before inserting into a main table named CHANGES.

Here is what I am dealing with table wise:

  1. There is a lookup table. This table basically gives the user the rules of data validation before it can be inserted into the MAIN table. The lookup table looks like this:

    ID  TABLECODE   COLUMNAME   ACCEPTEDDATATYPE    
    1   luDEPT      DEPTCODE    INT     
    2   luEMP       GENDERCODE  INT             
    3   luDEPT      BLDGcode    INT             
    

    So if a user is inserting an ID of 1, we know they are trying to make a correction to the DeptCode column and they must meet the requirements that only an Integer will be accepted before inserting into the CHANGES table (this is the main table that will hold the new values).

  2. CHANGES table - Data is inserted into this table with the new value per column. Data will only be inserted into this table if it passes validation against the lookup table and explained in part 3. Structure of CHANGES table

    ID  pkid    NEWVALUE    
    1   67        01
    1   84        09
    2   56        03
    
  3. This is the part I would like some help/input with to even see if it's doable. The column from the LOOKUP table name TABLECODE is the name of an actual table that exists in the database with codes and description for each column. So for example, all the DEPTCODE codes will be found in a lookup table named: luDEPT

Here is how the luDEPT that looks like this:

CODE  DEPARTMENTNAME
01    BIOLOGY
02    CHEMISTRY
03    INFORMATION TECHNOLOGY

So another validation step I have to take is, make sure that the NEW VALUE being inserted into CHANGES table is a valid code found in the lookup table related to the COLUMNNAME.

This is what I have so far, which works

CREATE PROCEDURE [dbo].[NewValueData]
(

@ID int,
@pkid VARCHAR(40),
@value VARCHAR(50)


)
AS
Begin
declare @result bit = 0;
declare @result1 bit = 0;
declare @result2 bit = 0;
declare @result3 bit = 0;
declare @result4 bit = 0;
    
DECLARE  @tablename varchar(50);

DECLARE @columndatatype varchar(30);
set @columndatatype=(select accepteddatatype from lookup where ID=@ID)

**set @tablename=(select TABLE_NAME from INFORMATION_SCHEMA.TABLES A, lookup b 
                where a.TABLE_NAME= b.lutablecode
                and TABLE_SCHEMA = 'Test'  and ID=@ID)**

   --CHECK IF ID, pkid and VALUE are PROVIDED
if (@pkid IS NULL OR @pkid = '') or (@ID IS NULL OR @ID = '') or (@value IS NULL OR @value = 
'')
begin
    set @result = 1
    PRINT 'PKID,ID or Value is missing'
end 


--CHECK IF ID EXISTS IN LOOKUP TABLE
if @ID not in (select ID from lookup 
    where @ID=ID)
begin
    set @result1=1
    PRINT 'ID is not in lookup table'
end

--IF datatype is an integer, only accept a numeric value
if @columndatatype = 'INT'
begin
    set @result3 = IIF(ISNUMERIC(@value)=1,0,1) 
    PRINT 'column type is INT '
end 
  
   **--ATTEMPT of trying to use @tablename 
    --CHECK IF VALUE IS AN ACCEPTED VALUE IN THE LOOKUP TABLE FOR THAT COLUMN
if @value not in (select code from @tablename where @value=code)
begin
    set @result4=1
    PRINT 'Not a valid code')
end**



if (@result = 0 and @result1 = 0 and @result2 = 0 and @result3 = 0 and @result4 = 0)
begin
    BEGIN TRANSACTION;
        begin try
    INSERT INTO [CHANGES] (ID, pkid,newvalue) VALUES (@ID, @pkid, @value)
PRINT 'New Record Inserted'
COMMIT TRANSACTION
end TRY
    begin catch
ROLLBACK TRANSACTION 
  PRINT 'id is not acceptable'
END
end
GO

The text in bold is my attempt at trying to derive the tablename dynamically but it doesn't work. Does anyone have suggestion on how to go about this issue? Any help will be welcomed.

Thom A
  • 88,727
  • 11
  • 45
  • 75
zephyr223
  • 15
  • 5
  • 1
    You've done a great job of describing the entire problem, however the problem you are facing gets lost in the rest of the detail. I recommend simplifying the question down to the minimum required to fix your issue, which is probably something like given the id of a row in my table how can I get the table name from the system tables. And then provide the data relevant to that. – Dale K Feb 16 '22 at 20:43
  • 1
    Does this answer your question? [A table name as a variable](https://stackoverflow.com/questions/2838490/a-table-name-as-a-variable) (Caveat, don't use the accepted answer, it's dangerous, use this [answer](https://stackoverflow.com/a/23317992/2029983)). – Thom A Feb 16 '22 at 20:52
  • I think you will need to use dynamic SQL for this. You will also need the OUTPUT clause to pass back the value of @result4 to the main scope. – Robert Sievers Feb 16 '22 at 20:53
  • 1
    Why are you doing this? If this is merely to audit changes, it is a very bad way to do it. – Stu Feb 16 '22 at 21:08
  • Also, your first lookup table is redundant -- you can look that stuff up in `INFORMATION_SCHEMA.COLUMNS`. – Stu Feb 16 '22 at 21:10
  • 1
    `begin catch ROLLBACK TRANSACTION PRINT 'id is not acceptable' END` You've just eaten every exception and converted it into a "message" (and a highly presumptuous one). That is terrible error handling. – SMor Feb 16 '22 at 21:27
  • 1
    I'm confused about why you would want to do all this: it sounds like the [inner platform effect](https://en.wikipedia.org/wiki/Inner-platform_effect) where you are effectively reinventing a database. Your code should already pre-define what columns are available and what is acceptable to them using constraints and possibly code-first entities – Charlieface Feb 16 '22 at 21:37
  • @stu I agree with you, but these were the requirements I was given to work with. Not my design. – zephyr223 Feb 17 '22 at 12:24
  • @SMor Thank you for pointing this out.. I have not even gotten to this part to fix this code. I should have said this is my first stored procedure I am writing also so forgive the terrible code. – zephyr223 Feb 17 '22 at 12:26
  • Thanks everyone for replying and for the ideas on how to go about it. I know it's sloppy and very weird requirements to deal with. – zephyr223 Feb 17 '22 at 12:36

1 Answers1

0

Try something like:

DECLARE @tablename sysname = 'luDEPT'
DECLARE @columnname sysname = 'DEPTCODE'
DECLARE @value INT = 123
DECLARE @valid BIT

DECLARE @sql NVARCHAR(MAX) = '
    SET @Valid = CASE
        WHEN EXISTS(SELECT * FROM ' + QUOTENAME(@tablename) + ' WHERE ' + QUOTENAME(@columnname) + ' = @Value)
        THEN 1
        ELSE 0
        END'
EXECUTE sp_executesql
    @sql,
    N'@value INT, @valid BIT OUTPUT',
    @value = @value, @valid = @valid OUTPUT

SELECT Valid = @Valid

The data type could potentially also be parameterized if types other than INT are needed.

T N
  • 4,322
  • 1
  • 5
  • 18