Checking for table columns before adding them in SQL Server - sql

I need to script out a table modification at work. Rather than do a simple "if exists then drop and create", they want it to check for the new columns I'm adding, and only then alter the table with them if they don't exist.
Could someone help me with the script? Assume a simple table that looks like this currently:
CREATE TABLE myTable (
[ID] [int] NOT NULL,
[FirstName] [varchar] (20) NOT NULL,
[LastName] [varchar] (20) NOT NULL
)
.. I'd like to add an Address field, varchar(50) let's say, but only if it doesn't already exist in the schema.

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'myTable' AND COLUMN_NAME = 'Address')
BEGIN
ALTER TABLE [dbo].[myTable] ADD
[Address] varchar(50) NOT NULL
END

Try this
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[myTable ]') AND type in (N'U'))
BEGIN
DROP TABLE [dbo].[myTable ]
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE myTable (
[ID] [int] NOT NULL,
[FirstName] [varchar] (20) NOT NULL,
[LastName] [varchar] (20) NOT NULL,
[Address] [varchar] (50) NOT NULL
)
OR
if not exists(select * from sys.columns
where Name = N'Address' and Object_ID = Object_ID(N'myTable'))
begin
alter table myTable
add Address varchar(50) NOT NULL
end
GO

Try this:
Here using it you can make check for multiple columns and get you table altered...
DECLARE #query VARCHAR(MAX)
IF EXISTS(SELECT * FROM sys.columns
WHERE Name = N'Address' AND OBJECT_ID = OBJECT_ID(N'<TableName>'))
BEGIN
SET
#query = 'ALTER TABLE <TableName> ADD Address varchar(50) GO'
END
--You can give multiple If conditions, example:
--IF EXISTS(SELECT * FROM sys.columns
--WHERE Name = N'<SomeOtherColumn>' AND OBJECT_ID = OBJECT_ID(N'<TableName>'))
--BEGIN
--SET
--#query = #query + 'ALTER TABLE <TableName> ADD <SomeOtherColumn> varchar(50) GO'
--END
EXEC sp_Executesql #query

IF 'col_name'
NOT IN
SELECT Name
FROM table_name.columns
ALTER TABLE table_name
ADD 'col_name' VARCHAR(65) NOT NULL
something like this?
EDIT: my friend believes this is much better
IF NOT EXISTS
(SELECT Name
FROM table_name.columns
Where Name='target_column_name')
ALTER TABLE table_name
ADD 'target_column_name' VARCHAR(65) NOTNULL

Related

Foreign Key Error in Oracle to SQL Server Migration

So I have been tasked with converting an Oracle DB to SQL Server. I have never worked with DB's before so I am just running MS SQL Server Migration Assistant to start and only received one error regarding foreign keys. I have done some research but don't understand the principal enough to know how the change needs to be made. Below are the Oracle and SQL Tables from the migration. At the very bottom is the error on the SQL Server side. If anyone could lead me in the right direction I would appreciate it.
Oracle Table:
CREATE TABLE XYZUSER.XYZCMDARGS
(
CMDROWKEY NUMBER(10, 0) NOT NULL,
CMDARGNUM NUMBER(3, 0) NOT NULL,
MEMBERNAME VARCHAR2(50) NOT NULL,
MEMBERTYPE NUMBER(1, 0) NOT NULL,
ARGFORMAT NUMBER(1, 0) NOT NULL,
STARTBIT NUMBER(2, 0) NOT NULL,
TOTALBITS NUMBER(2, 0) NOT NULL,
MSBORLSBFIRST CHAR(1) NOT NULL,
MEMBERVALUE NUMBER(20, 0) NOT NULL,
STATELIST VARCHAR2(200) NOT NULL,
RANGEHIGHFLOAT FLOAT(52) NOT NULL,
RANGELOWFLOAT FLOAT(52) NOT NULL,
RANGEHIGHINT NUMBER(20, 0) NOT NULL,
RANGELOWINT NUMBER(20, 0) NOT NULL,
UNITS CHAR(8) NOT NULL,
INCRPERBIT FLOAT(52) NOT NULL,
ARGPROCESSING NUMBER(1, 0) NOT NULL
);
ALTER TABLE XYZUSER.XYZCMDARGS ADD CONSTRAINT XYZCMDARGS_PK
PRIMARY KEY (CMDROWKEY, CMDARGNUM);
ALTER TABLE XYZUSER.XYZCMDARGS ADD CONSTRAINT XYZCMDARGS_CMDROWKEY_FK
FOREIGN KEY (CMDROWKEY)
REFERENCES XYZUSER.XYZCMDS (CMDROWKEY);
SQL Server:
IF EXISTS (SELECT * FROM sys.objects so JOIN sys.schemas sc ON so.schema_id = sc.schema_id WHERE so.name = N'XYZCMDARGS' AND sc.name = N'XYZUSER' AND type in (N'U'))
BEGIN
DECLARE #drop_statement nvarchar(500)
DECLARE drop_cursor CURSOR FOR
SELECT 'alter table '+quotename(schema_name(ob.schema_id))+
'.'+quotename(object_name(ob.object_id))+ ' drop constraint ' + quotename(fk.name)
FROM sys.objects ob INNER JOIN sys.foreign_keys fk ON fk.parent_object_id = ob.object_id
WHERE fk.referenced_object_id =
(
SELECT so.object_id
FROM sys.objects so JOIN sys.schemas sc
ON so.schema_id = sc.schema_id
WHERE so.name = N'XYZCMDARGS' AND sc.name = N'XYZUSER' AND type in (N'U')
)
OPEN drop_cursor
FETCH NEXT FROM drop_cursor
INTO #drop_statement
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC (#drop_statement)
FETCH NEXT FROM drop_cursor
INTO #drop_statement
END
CLOSE drop_cursor
DEALLOCATE drop_cursor
DROP TABLE [XYZUSER].[XYZCMDARGS]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE
[XYZUSER].[XYZCMDARGS]
(
[CMDROWKEY] numeric(10, 0) NOT NULL,
[CMDARGNUM] numeric(3, 0) NOT NULL,
[MEMBERNAME] varchar(50) NOT NULL,
[MEMBERTYPE] numeric(1, 0) NOT NULL,
[ARGFORMAT] numeric(1, 0) NOT NULL,
[STARTBIT] numeric(2, 0) NOT NULL,
[TOTALBITS] numeric(2, 0) NOT NULL,
[MSBORLSBFIRST] char(1) NOT NULL,
[MEMBERVALUE] numeric(20, 0) NOT NULL,
[STATELIST] varchar(200) NOT NULL,
[RANGEHIGHFLOAT] float(52) NOT NULL,
[RANGELOWFLOAT] float(52) NOT NULL,
[RANGEHIGHINT] numeric(20, 0) NOT NULL,
[RANGELOWINT] numeric(20, 0) NOT NULL,
[UNITS] char(8) NOT NULL,
[INCRPERBIT] float(52) NOT NULL,
[ARGPROCESSING] numeric(1, 0) NOT NULL
)
WITH (DATA_COMPRESSION = NONE)
GO
IF EXISTS (SELECT * FROM sys.objects so JOIN sys.schemas sc ON so.schema_id = sc.schema_id WHERE so.name = N'XYZCMDARGS_PK' AND sc.name = N'XYZUSER' AND type in (N'PK'))
ALTER TABLE [XYZUSER].[XYZCMDARGS] DROP CONSTRAINT [XYZCMDARGS_PK]
GO
ALTER TABLE [XYZUSER].[XYZCMDARGS]
ADD CONSTRAINT [XYZCMDARGS_PK]
PRIMARY KEY
CLUSTERED ([CMDROWKEY] ASC, [CMDARGNUM] ASC)
GO
IF EXISTS (SELECT * FROM sys.objects so JOIN sys.schemas sc ON so.schema_id = sc.schema_id WHERE so.name = N'XYZCMDARGS_CMDROWKEY_FK' AND sc.name = N'XYZUSER' AND type in (N'F'))
ALTER TABLE [XYZUSER].[XYZCMDARGS] DROP CONSTRAINT [XYZCMDARGS_CMDROWKEY_FK]
GO
/*
* SSMA error messages:
* O2SS0231: Foreign keys with different types of columns and referenced columns cannot be converted:XYZCMDARGS_CMDROWKEY_FK.
ALTER TABLE [XYZUSER].[XYZCMDARGS]
ADD CONSTRAINT [XYZCMDARGS_CMDROWKEY_FK]
FOREIGN KEY
([CMDROWKEY])
REFERENCES
[ACEDB1A].[XYZUSER].[XYZCMDS] ([CMDROWKEY])
ON DELETE NO ACTION
ON UPDATE NO ACTION
*/
The issue appears to be that the parent table column XYZUSER.XYZCMDS (CMDROWKEY) is declared as a NUMBER(9,0) while the child table column XYZUSER.XYZCMDARGS (CMDROWKEY) is declared as a NUMBER(10,0). Since the child table declared a larger column than the parent table allows, it is probably safe to change the child table column to a NUMBER(9,0) (assuming nothing weird is going on where the child table was allowed to store values that are larger than what the parent table allows). Alternately, you could redefine the parent table column as a NUMBER(10,0) but then you'd need to redefine the column in any other child tables that reference this parent.

Creating a single table for two different entities

What is an appropriate way to structure the database table for the following scenario?
I'm trying to model an incident report, part of it will be recording whether (as part of the incident) an employee or customer had to receive medical attention. The same details are to be recorded for both. In the paper version of the form and in the interface, it is shown as a single table. The table headings would be:
Person (either employee or customer)
Injured
First aid administered
Hospitalised
Details
We already have separate employee and customer tables which old their personal details. The new table would include the ID, details on whether the person was injured, received first aid, hospitalised and any other additional details.
So I've been pondering on a few possibilities of how this could be structured:
Two separate tables for employee and customer incidents. The two tables will hold mostly the same fields with the exception of the foreign keys for EmployeeID or CustomerID.
A single table that includes all the incident related fields and additional fields for EmployeeID and CustomerID where these are nullable foreign keys to the primary keys of the Employee and Customer table respectively. A constraint to ensure that EmployeeID XOR CustomerID is not null.
Create 3 new tables: one to just hold the incidents, one to link customer ID with an incident and another to link employee ID with an incident - is this too normalised? (assuming I will never have more than customers or employees to record incident details for)
A single table that includes all the incident related fields with a single field to store either the employee ID or customer ID and a type field to specify whether it's employee or customer.
I'm leaning towards option 1, it seems to be the cleanest and should different fields need to be recorded for customers and employees, it's easy to update a single table without affecting the other. I've discounted option 4, I think this is bad design since I'm not able to add a FK constraint to the ID column.
Are there any reasons why I shouldn't go for option 1 or are there better alternatives I should also consider?
I would create "link" tables. And not repeat the DDL for the Incident.
then you have the ability to create a view which would bring together customers and employees with the incident data.
I agree with the previous answer to separate the incident details.
-- START TSQL
SET NOCOUNT ON
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'vwIncidents' and TABLE_TYPE = N'VIEW' )
BEGIN
DROP VIEW [dbo].[vwIncidents]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'CustomerToIncidentLink' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[CustomerToIncidentLink]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'EmployeeToIncidentLink' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[EmployeeToIncidentLink]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Incident' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[Incident]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Employee' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[Employee]
END
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Customer' and TABLE_TYPE = N'BASE TABLE' )
BEGIN DROP TABLE [dbo].[Customer]
END
CREATE TABLE [dbo].[Employee] (
[EmployeeUUID] [uniqueidentifier] NOT NULL,
[TheVersionProperty] [timestamp] NOT NULL,
[SSN] [nvarchar](11) NOT NULL,
[LastName] [varchar](64) NOT NULL,
[FirstName] [varchar](64) NOT NULL,
[CreateDate] [datetime] NOT NULL,
[HireDate] [datetime] NOT NULL
)
GO
ALTER TABLE dbo.Employee ADD CONSTRAINT PK_Employee PRIMARY KEY NONCLUSTERED (EmployeeUUID)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT CK_SSN_Unique UNIQUE (SSN)
GO
CREATE TABLE [dbo].[Customer] (
[CustomerUUID] [uniqueidentifier] NOT NULL,
[TheVersionProperty] [timestamp] NOT NULL,
[CustomerNumber] [nvarchar](11) NOT NULL,
[LastName] [varchar](64) NOT NULL,
[FirstName] [varchar](64) NOT NULL,
[CreateDate] [datetime] NOT NULL
)
GO
ALTER TABLE dbo.Customer ADD CONSTRAINT PK_Customer PRIMARY KEY NONCLUSTERED (CustomerUUID)
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT CK_CustomerNumber_Unique UNIQUE (CustomerNumber)
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[Incident]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN DROP TABLE [dbo].[Incident]
END
GO
CREATE TABLE [dbo].[Incident]
(
IncidentUUID [UNIQUEIDENTIFIER] NOT NULL DEFAULT NEWSEQUENTIALID()
, IncidentName varchar(24) not null
, CreateDate smalldatetime not null
)
GO
ALTER TABLE dbo.Incident ADD CONSTRAINT PK_Incident PRIMARY KEY NONCLUSTERED (IncidentUUID)
GO
ALTER TABLE [dbo].[Incident] ADD CONSTRAINT CK_IncidentName_Unique UNIQUE (IncidentName)
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[EmployeeToIncidentLink]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN DROP TABLE [dbo].[EmployeeToIncidentLink]
END
GO
CREATE TABLE [dbo].[EmployeeToIncidentLink] (
[LinkSurrogateUUID] [uniqueidentifier] NOT NULL,
[TheEmployeeUUID] [uniqueidentifier] NOT NULL,
[TheIncidentUUID] [uniqueidentifier] NOT NULL
)
GO
ALTER TABLE dbo.EmployeeToIncidentLink ADD CONSTRAINT PK_EmployeeToIncidentLink PRIMARY KEY NONCLUSTERED (LinkSurrogateUUID)
GO
ALTER TABLE [dbo].[EmployeeToIncidentLink] ADD CONSTRAINT FK_EmployeeToIncidentLinkToEmployee FOREIGN KEY (TheEmployeeUUID) REFERENCES dbo.Employee (EmployeeUUID)
GO
ALTER TABLE [dbo].[EmployeeToIncidentLink] ADD CONSTRAINT FK_EmployeeToIncidentLinkToIncident FOREIGN KEY (TheIncidentUUID) REFERENCES dbo.Incident (IncidentUUID)
GO
ALTER TABLE [dbo].[EmployeeToIncidentLink] ADD CONSTRAINT CONST_UNIQUE_EmpUUID_InciUUID UNIQUE (TheEmployeeUUID , TheIncidentUUID)
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[CustomerToIncidentLink]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN DROP TABLE [dbo].[CustomerToIncidentLink]
END
GO
CREATE TABLE [dbo].[CustomerToIncidentLink] (
[LinkSurrogateUUID] [uniqueidentifier] NOT NULL,
[TheCustomerUUID] [uniqueidentifier] NOT NULL,
[TheIncidentUUID] [uniqueidentifier] NOT NULL
)
GO
ALTER TABLE dbo.CustomerToIncidentLink ADD CONSTRAINT PK_CustomerToIncidentLink PRIMARY KEY NONCLUSTERED (LinkSurrogateUUID)
GO
ALTER TABLE [dbo].[CustomerToIncidentLink] ADD CONSTRAINT FK_CustomerToIncidentLinkToCustomer FOREIGN KEY (TheCustomerUUID) REFERENCES dbo.Customer (CustomerUUID)
GO
ALTER TABLE [dbo].[CustomerToIncidentLink] ADD CONSTRAINT FK_CustomerToIncidentLinkToIncident FOREIGN KEY (TheIncidentUUID) REFERENCES dbo.Incident (IncidentUUID)
GO
ALTER TABLE [dbo].[CustomerToIncidentLink] ADD CONSTRAINT CONST_UNIQUE_CustomerUUID_InciUUID UNIQUE (TheCustomerUUID , TheIncidentUUID)
GO
CREATE VIEW dbo.vwIncidents
AS
/* combine what is in common with employees and customers for this view */
select e.LastName, e.FirstName, inc.IncidentName
from dbo.Employee e
join
dbo.EmployeeToIncidentLink link on e.EmployeeUUID = link.TheEmployeeUUID
join
dbo.Incident inc on inc.IncidentUUID = link.TheIncidentUUID
UNION ALL
select c.LastName, c.FirstName, inc.IncidentName
from dbo.Customer c
join
dbo.CustomerToIncidentLink link on c.CustomerUUID = link.TheCustomerUUID
join
dbo.Incident inc on inc.IncidentUUID = link.TheIncidentUUID
GO
There is another option (I think?)
Create a Person table, (this has info in common with customers and employees) and then create "sub class" tables for customer and employee that has unique to that entity data.
That's a topic unto itself.
See
How do we implement an IS-A Relationship?
"IS A"
or "how to subclass in a database" would be search tips.
I have this example that is close to yours, that does a "subclassing"
-- START TSQL
SET NOCOUNT ON
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'vwPersonEmail' and TABLE_TYPE = N'VIEW' )
BEGIN
DROP VIEW [dbo].[vwPersonEmail]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'PersonEmail' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[PersonEmail]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Customer' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[Customer]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Employee' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[Employee]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'PersonSuperType' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[PersonSuperType]
END
GO
IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = N'dbo' and TABLE_NAME = N'Department' and TABLE_TYPE = N'BASE TABLE' )
BEGIN
DROP TABLE [dbo].[Department]
END
GO
CREATE TABLE [dbo].[Department](
[DepartmentUUID] [uniqueidentifier] NOT NULL,
[TheVersionProperty] [timestamp] NOT NULL,
[DepartmentName] [nvarchar](80) NULL,
[CreateDate] [datetime] NOT NULL,
[MyTinyInt] tinyint not null
)
ALTER TABLE dbo.[Department] ADD CONSTRAINT PK_Department PRIMARY KEY NONCLUSTERED ([DepartmentUUID])
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT CK_DepartmentName_Unique UNIQUE ([DepartmentName])
GO
CREATE TABLE [dbo].[PersonSuperType] (
[PersonSuperTypeUUID] [uniqueidentifier] not null default NEWSEQUENTIALID(),
[LastName] [varchar](64) NOT NULL,
[FirstName] [varchar](64) NOT NULL
)
GO
ALTER TABLE dbo.PersonSuperType ADD CONSTRAINT PK_PersonSuperTypeUUID PRIMARY KEY CLUSTERED (PersonSuperTypeUUID)
GO
CREATE TABLE [dbo].[Employee] (
[EmployeeUUID] [uniqueidentifier] NOT NULL,
[ParentDepartmentUUID] [uniqueidentifier] NOT NULL,
[TheVersionProperty] [timestamp] NOT NULL,
[SSN] [nvarchar](11) NOT NULL,
[CreateDate] [datetime] NOT NULL,
[HireDate] [datetime] NOT NULL
)
GO
ALTER TABLE dbo.Employee ADD CONSTRAINT PK_Employee PRIMARY KEY NONCLUSTERED (EmployeeUUID)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT CK_SSN_Unique UNIQUE (SSN)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT FK_EmployeeToDepartment FOREIGN KEY (ParentDepartmentUUID) REFERENCES dbo.Department (DepartmentUUID)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT FK_EmployeeToPersonSuperType FOREIGN KEY (EmployeeUUID) REFERENCES dbo.PersonSuperType (PersonSuperTypeUUID)
GO
CREATE TABLE [dbo].[Customer] (
[CustomerUUID] [uniqueidentifier] NOT NULL,
[TheVersionProperty] [timestamp] NOT NULL,
[CustomerNumber] [nvarchar](11) NOT NULL,
[CreateDate] [datetime] NOT NULL,
)
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT PK_Customer PRIMARY KEY NONCLUSTERED (CustomerUUID)
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT CK_Customer_CustomerNumber_Unique UNIQUE (CustomerNumber)
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT FK_CustomerToPersonSuperType FOREIGN KEY (CustomerUUID) REFERENCES dbo.PersonSuperType (PersonSuperTypeUUID)
GO
CREATE TABLE [dbo].[PersonEmail] (
[PersonEmailUUID] [uniqueidentifier] NOT NULL,
[PersonSuperTypeUUID] [uniqueidentifier] NOT NULL,
[EmailAddress] [varchar](256) NOT NULL
)
GO
ALTER TABLE [dbo].[PersonEmail] ADD CONSTRAINT PK_PersonEmail PRIMARY KEY NONCLUSTERED (PersonEmailUUID)
GO
ALTER TABLE [dbo].[PersonEmail] ADD CONSTRAINT CK_PersonEmail_EmailAddress_Unique UNIQUE (EmailAddress)
GO
ALTER TABLE [dbo].[PersonEmail] ADD CONSTRAINT FK_PersonEmailToPersonSuperType FOREIGN KEY (PersonSuperTypeUUID) REFERENCES dbo.PersonSuperType (PersonSuperTypeUUID)
GO
CREATE VIEW dbo.vwPersonEmail
AS
/* combine what is in common with employees and customers for this view */
select per.LastName, per.FirstName, [UniqueIdentifier] = emp.SSN, pemail.EmailAddress
from dbo.PersonSuperType per
join
dbo.Employee emp on per.PersonSuperTypeUUID = emp.EmployeeUUID
join
dbo.PersonEmail pemail on per.PersonSuperTypeUUID = pemail.PersonSuperTypeUUID
UNION ALL
select per.LastName, per.FirstName, [UniqueIdentifier] = cust.CustomerNumber, pemail.EmailAddress
from dbo.PersonSuperType per
join
dbo.Customer cust on per.PersonSuperTypeUUID = cust.CustomerUUID
join
dbo.PersonEmail pemail on per.PersonSuperTypeUUID = pemail.PersonSuperTypeUUID
GO
So now that I think about it, I might lean toward the "subclassing" way..since your two types of people might have alot of common attributes, and a few distinct ones.
If you want only to show information about 2 diferent tables in only one, you could use a View. Here you have an example check it out.
https://www.w3schools.com/sql/sql_view.asp => tutorial
https://www.ibm.com/support/knowledgecenter/en/ssw_i5_54/rzatc/rzatcviewmultsql.htm => example
I'd formalize the distinction between the incident and the details.
INCIDENT_HEADER
Customer ID <nullable, fk into the customer table>
Employee ID <nullable, fk into the employee table, which already exsits>
Summary
Date
Status (?)
INCIDENT_DETAIL
DETAIL_TYPE_ID <fk into a type table, it's up next...>
DESCRIPTION
DETAIL_TYPE
ID
DESCRIPTION
DETAIL_TYPE will end up with things like "Injury", "Hospitalized", and "First Aid". You can add more to that table later without touching the details table.
This design lets you add more kinds of details without altering the header table.

How to join data using dynamic table and field names

So I have some tables that contains a foreign key that refers to different tables and fields depending on the context. I have a key source table that contains the relevant table and field names. Then I need to be able to lookup the values in those tables/fields.
I simplified it to the following tables/data for demonstration of what I have:
CREATE TABLE [dbo].[WorkOrderStatus](
[WorkOrderStatusId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](60) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Note](
[NoteId] [int] IDENTITY(1,1) NOT NULL,
[NoteKeySourceId] [int] NULL,
[KeyValue] [int] NULL,
[Note] [nvarchar](max) NOT NULL,
) ON [PRIMARY]
CREATE TABLE [dbo].[NoteKeySource](
[NoteKeySourceId] [int] IDENTITY(1,1) NOT NULL,
[SourceTable] [nvarchar](60) NOT NULL,
[SourceField] [nvarchar](60) NOT NULL,
[SourceTextField] [nvarchar](60) NOT NULL,
) ON [PRIMARY]
GO
INSERT INTO WorkOrderStatus VALUES('Open')
DECLARE #OpenStatusId int = SCOPE_IDENTITY();
INSERT INTO WorkOrderStatus VALUES('Closed')
DECLARE #ClosedStatusId int = SCOPE_IDENTITY();
INSERT INTO NoteKeySource VALUES('WorkOrderStatus', 'WorkOrderStatusId', 'Name')
DECLARE #WorkOrderSource int = SCOPE_IDENTITY();
INSERT INTO Note VALUES(#WorkOrderSource, #OpenStatusId, 'Opened the work order')
INSERT INTO Note VALUES(#WorkOrderSource, #ClosedStatusId, 'Closed the work order')
So I need to be able to query a list of Notes, including the SourceTextField referred to in NoteKeySource, identified by KeyValue.
Here is the SP that I have now, which works:
CREATE PROCEDURE [dbo].[spGetNotes]
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
CREATE TABLE #NoteTable (
NoteId int NOT NULL,
SourceTable nvarchar(60),
SourceField nvarchar(60),
SourceTextField nvarchar(60),
KeyValue int,
Context nvarchar(MAX),
Note nvarchar(MAX),
)
INSERT INTO #NoteTable (NoteId, SourceTable, SourceField, SourceTextField, KeyValue, Note)
SELECT N.NoteId, NKS.SourceTable, NKS.SourceField, NKS.SourceTextField, N.KeyValue, N.Note
FROM Note AS N
LEFT OUTER JOIN NoteKeySource AS NKS
ON NKS.NoteKeySourceId = N.NoteKeySourceId
DECLARE #Context nvarchar(max)
DECLARE #Sql nvarchar(255);
DECLARE #SqlDecl nvarchar(255) = N'#Key int, #Ctx nvarchar(MAX) OUTPUT';
DECLARE #NoteId int, #KeyValue int, #SourceTable nvarchar(60), #SourceField nvarchar(60), #SourceTextField nvarchar(60)
DECLARE db_cursor CURSOR FOR SELECT SourceTable, SourceField, SourceTextField, KeyValue
FROM #NoteTable WHERE SourceTable IS NOT NULL FOR UPDATE OF Context
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #SourceTable, #SourceField, #SourceTextField, #KeyValue
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Sql = N'SELECT #Ctx = ' + #SourceTextField + ' FROM ' + #SourceTable + ' WHERE ' + #SourceField + '= #Key';
EXECUTE sp_executesql #Sql, #SqlDecl, #Key = #KeyValue, #Ctx = #Context OUTPUT
UPDATE #NoteTable SET Context=#Context WHERE CURRENT OF db_cursor
FETCH NEXT FROM db_cursor INTO #SourceTable, #SourceField, #SourceTextField, #KeyValue
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT NoteId, Context, Note from #NoteTable
DROP TABLE #NoteTable
RETURN 0
Calling spGetNotes returns:
1:Open:Opened the work order
2:Closed:Closed the work order
It works, but it is pretty ugly, and I would just like to know if there is a better/cleaner/more appropriate way to do this without using a cursor or a while loop. It is my understanding that dynamic SQL cannot be used in a subquery of normal SQL, so this was the best I could come up with.
I am relatively green when it comes to SQL, so if I am wrong about that, please enlighten me! Also, if there is a better overall approach to the general domain problem, that would also be welcome and appreciated :)
Create a view that provides a single EAV overlay across your source tables. Then join NoteKeySource to it.
CREATE VIEW AllSources AS (
SELECT
'SourceTable1' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable1
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table1Field1','Table1Field2','Table1Field3','Table1Field4')
) p
UNION ALL
SELECT
'SourceTable2' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable2
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table2Field1','Table2Field2')
) p
UNION ALL
SELECT
'SourceTable3' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable3
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table3Field1','Table3Field2','Table3Field3')
) p
--etc
)

Customer search stored procedure

I am writing a customer search stored procedure which having three parameters firstname, lastname, fathers name all are varchar
I wrote like below :
CREATE PROCEDURE [dbo].[SearchCustomer]
(
#p_FirstName varchar = NULL
,#p_LastName varchar = NULL
,#p_FatherFirstName varchar = NULL
)
AS
BEGIN
SET NOCOUNT ON
SELECT [CustomerID]
,[CustomerTitle]
,[FirstName]
,[MiddleName]
,[LastName]
,[FatherFirstName]
,[EmailId]
FROM [Customer]
WHERE [FirstName] LIKE COALESCE(#p_FirstName,'%')
OR [LastName] LIKE COALESCE #p_LastName,'%')
OR [FatherFirstName] LIKE COALESCE(#p_FatherFirstName,'%')
END
I am using optional parameters because if there is no parameter value I need to get full listing without any criteria but when I give just first name I need only those rows which matching that criteria not the rest, but now its listing all
I guess the problem is somewhere near OR
Could you please help?
If i change OR to AND
now if i put first name as 'abc' and last name and father name black ...
i am getting all listing including 'abc' as first name..
i need only the row which having first name as 'abc' not the rest
This will filter rows depending on which parameters were submitted:
CREATE PROCEDURE [dbo].[SearchCustomer]
(
#p_FirstName varchar(50) = NULL
,#p_LastName varchar(50) = NULL
,#p_FatherFirstName varchar(50) = NULL
)
AS
BEGIN
SET NOCOUNT ON
SELECT [CustomerID]
,[CustomerTitle]
,[FirstName]
,[MiddleName]
,[LastName]
,[FatherFirstName]
,[EmailId]
FROM [Customer]
WHERE [FirstName] LIKE COALESCE(#p_FirstName + '%', FirstName)
AND [LastName] LIKE COALESCE (#p_LastName + '%', LastName)
AND [FatherFirstName] LIKE COALESCE(#p_FatherFirstName + '%', FatherFirstName)
END
GO
Use where clause like the example below .
WHERE (#p_FirstName='' or [FirstName] LIKE COALESCE(#p_FirstName,'%'))
OR (#p_LastName='' or [LastName] LIKE COALESCE #p_LastName,'%'))
OR (#p_FatherFirstName='' or [FatherFirstName] LIKE COALESCE(#p_FatherFirstName,'%'))
and set all your parameters '' if they are NULL by using following syntax.
SET #p_FirstName=ISNULL(#p_FirstName,'')
CREATE PROCEDURE [dbo].[SearchCustomer]
(
#p_FirstName varchar = NULL
,#p_LastName varchar = NULL
,#p_FatherFirstName varchar = NULL
)
AS
BEGIN
SET NOCOUNT ON
SELECT [CustomerID]
,[CustomerTitle]
,[FirstName]
,[MiddleName]
,[LastName]
,[FatherFirstName]
,[EmailId]
FROM [Customer]
WHERE [FirstName] LIKE COALESCE(#p_FirstName,'%')
OR (FirstName is NULL
AND [LastName] LIKE COALESCE(#p_LastName,'%')
AND [FatherFirstName] LIKE COALESCE(#p_FatherFirstName,'%'))
END
CREATE PROCEDURE [dbo].[SearchCustomer]
(
#p_FirstName varchar(50) = NULL
,#p_LastName varchar(50) = NULL
,#p_FatherFirstName varchar(50) = NULL
)
AS
BEGIN
SET NOCOUNT ON
SELECT [CustomerID]
,[CustomerTitle]
,[FirstName]
,[MiddleName]
,[LastName]
,[FatherFirstName]
,[EmailId]
FROM [Customer]
WHERE ([FirstName] = #p_FirstName OR #p_FirstName IS NULL)
AND ([LastName] = #p_LastName OR #p_LastName IS NULL)
AND ([FatherFirstName] = #p_FatherFirstName OR #p_FatherFirstName IS NULL)
END

Return NEWSEQUENTIALID() as an output parameter

Imagine a table that looks like this:
CREATE TABLE [dbo].[test](
[id] [uniqueidentifier] NULL,
[name] [varchar](50) NULL
)
GO
ALTER TABLE [dbo].[test] ADD CONSTRAINT [DF_test_id] DEFAULT (newsequentialid()) FOR [id]
GO
With an INSERT stored procedure that looks like this:
CREATE PROCEDURE [Insert_test]
#name as varchar(50),
#id as uniqueidentifier OUTPUT
AS
BEGIN
INSERT INTO test(
name
)
VALUES(
#name
)
END
What is the best way to get the GUID that was just inserted and return it as an output parameter?
Use the Output clause of the Insert statement.
CREATE PROCEDURE [Insert_test]
#name as varchar(50),
#id as uniqueidentifier OUTPUT
AS
BEGIN
declare #returnid table (id uniqueidentifier)
INSERT INTO test(
name
)
output inserted.id into #returnid
VALUES(
#name
)
select #id = r.id from #returnid r
END
GO
/* Test the Procedure */
declare #myid uniqueidentifier
exec insert_test 'dummy', #myid output
select #myid
Try
SELECT #ID = ID FROM Test WHERE Name = #Name
(if Name has a Unique constraint)