How to store multiple message threads in CRM database - tsql

I'm working with a Customer Relationship Management application. I have two tables: tbl_Inquiry_master for storing inquiry question details raised by end users, and tbl_Inquiry_History for storing inquiry answer details.
However this shows knowledge to store one question store to table tbl_Inquiry_master and answers that are given by staff to that inquiry stored in table tbl_Inquiry_History.
For more information I represent table tbl_Inquiry_master schema:
Column Name Data Type
_________________________________________
Id varchar(50)
Inquiry_subject varchar(100)
Status_id numeric(18, 0)
Created_date datetime
Priority_id numeric(18, 0)
User_id varchar(50)
Email_Address varchar(50)
Service_id numeric(18, 0)
Inquiry_Content varchar(1024)
TimeStamp datetime
Table tbl_Inquiry_History schema:
Column Name Data Type
_________________________________________
Id numeric(18, 0)
Inquiry_id varchar(50)
Inquiry_subject varchar(50)
Service_id numeric(18, 0)
Priority_id numeric(18, 0)
User_id varchar(50)
Status_id numeric(18, 0)
Inquiry_desc varchar(1024)
IsDisplay bit
IsRead bit
IsReplied bit
TimeStamp datetime
Activity_start_time datetime
Activity_expire_time datetime
Table tbl_User_master schema:
Column Name Data Type PK/FK Reg Table Ref Key
____________________________________________________________________________________
Id varchar(50) PK - -
User_id varchar(50) FK tbl_Login_master Id
Full_Name varchar(50)
.
.
Email_Address varchar(50)
Table tbl_Question schema:
Column Name DatType PK/FK Ref Table Ref Key
____________________________________________________________________
Id int PK - -
UserId varchar(50) FK tbl_User_master Id
Body varchar(1024)
Inquiry_Id varchar(50) FK tbl_Inquiry_master Id
Table tbl_Answer schema:
Column Name DatType PK/FK Ref Table Ref Key
____________________________________________________________________
Id int PK - -
QuestionId int FK tbl_Question Id
Body varchar(1024)
Inquiry_Id varchar(50) FK tbl_Inquiry_master Id
However I don't know how I can store multiple Inquiry's questions(raised by end users) and multiple Inquiry's answers (given by staff user).
i include this stored procedure for how i insert new inquiry generated from End User and how info stores to table hierarchy.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[InsertInquiry]
(
#Inquiry_subject VARCHAR(50),
#Service_name VARCHAR(50),
#Priority_type VARCHAR(25),
#User_id VARCHAR(50),
#Inquiry_desc VARCHAR(1024),
#email VARCHAR(50),
#NewId VARCHAR(50) OUT
)
AS
SET NOCOUNT ON;
declare #var1 int
declare #var2 int
declare #var3 int
declare #uniqueRef char(14)
set #uniqueRef = dbo.UniqueRefNum(rand(), rand(), rand(), rand())
BEGIN TRAN;
BEGIN TRY
SET #var1= (SELECT [Id] FROM [OmStocks].[dbo].[tbl_Status_master] WHERE (Status_name='Open'))
SET #var2= (SELECT [Id] FROM [OmStocks].[dbo].[tbl_Service_master] WHERE (Service_name=#Service_name))
SET #var3= (SELECT [Id] FROM [OmStocks].[dbo].[tbl_Priority_master] WHERE (Priority_name=#Priority_type))
INSERT INTO [OmStocks].[dbo].[tbl_Inquiry_master]
([Id]
,[Inquiry_subject]
,[Status_id]
,[Created_date]
,[Priority_id]
,[User_id]
,[Email_Address]
,[Service_id]
,[Inquiry_desc])
VALUES
(#uniqueRef,#Inquiry_subject,#var1,CONVERT(DATETIME,GETDATE(), 101),#var3,#User_id,#email,#var2,#Inquiry_desc)
INSERT INTO [OmStocks].[dbo].[tbl_Question]
([UserId],[Body],[Inquiry_Id])
VALUES
(#User_id,#Inquiry_desc,#uniqueRef)
INSERT INTO [OmStocks].[dbo].[tbl_Inquiry_History]
([Inquiry_id]
,[Inquiry_subject]
,[Service_id]
,[Priority_id]
,[User_id]
,[Status_id]
,[Inquiry_desc]
,[IsDisplay]
,[IsRead]
,[IsReplied]
,[Activity_start_time])
VALUES
(#uniqueRef,#Inquiry_subject,#var2,#var3,#User_id,#var1,#Inquiry_desc,0,0,0,CONVERT(DATETIME,GETDATE(), 101))
SET #NewId= #uniqueRef
COMMIT TRAN;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
-- Raise the error with the appropriate message and error severity
DECLARE #ErrMsg nvarchar(4000), #ErrSeverity int;
SELECT #ErrMsg = ERROR_MESSAGE(), #ErrSeverity = ERROR_SEVERITY();
RAISERROR(#ErrMsg, #ErrSeverity, 1);
END CATCH;
Can someone suggest me the changes I need to make to my schema to store data for this scenario?

In simple terms you need a User table, a Question table and an Answer table which form a hierarchy in that order. Each Answer row has a foreign key back to the Question it relates to. Each Question row has a foreign key back to the User that asked the question (I imagine a User would have a foreign key back to a customer/contract, etc.). You would then decorate each entity with the attributes that ONLY relate to that entity. Your history table appears to be more of an audit trail at the moment, rather than an Answer table (lots of de-normalised columns from the master table).
Something like:-
User table
---------------------------
Id int
Email varchar(50)
Question table
---------------------------
Id int
UserId int
Body varchar(1024)
Answer table
---------------------------
Id int
QuestionId int
Body varchar(1024)
Then you add the columns from your schema at the appropriate level. Does Status_id describe the status of the question or the answer, for example. I imagine Priority_id relates to the Question and not to the Answer, so it should only exist in the Question table.
Without knowing the actual use of each column, it's difficult to give a more specific answer that relates directly to your schema.

I'm still unclear what goes into the text fields on master and history (and whether the history table should be part of this hierarchy) but - if you use the following as an example:-
create table tbl_Inquiry_master (
id int,
body varchar(1024)
);
create table tbl_Inquiry_history (
id int,
inquiry_id int,
body varchar(1024)
);
create table tbl_question (
id int,
inquiry_id int,
body varchar(1024)
);
create table tbl_answer (
id int,
question_id int,
body varchar(1024)
);
insert into tbl_Inquiry_master values (1, 'inquiry one');
insert into tbl_Inquiry_master values (2, 'inquiry two');
insert into tbl_Inquiry_master values (3, 'inquiry three');
insert into tbl_Inquiry_master values (4, 'inquiry four');
insert into tbl_Inquiry_history values (1, 1,'history 1 (relates to inquiry 1)');
insert into tbl_Inquiry_history values (2, 3,'history 2 (relates to inquiry 3)');
insert into tbl_question values (1,1,'inquiry one question one')
insert into tbl_question values (2,1,'inquiry one question two')
insert into tbl_question values (3,2,'inquiry two question one')
insert into tbl_question values (4,4,'inquiry four question one')
insert into tbl_answer values (1,1,'answer 1 to question 1')
insert into tbl_answer values (2,1,'answer 2 to question 1')
insert into tbl_answer values (3,2,'answer 1 to question 2')
insert into tbl_answer values (4,4,'answer 1 to question 4')
insert into tbl_answer values (5,4,'answer 2 to question 4')
insert into tbl_answer values (6,4,'answer 3 to question 4')
select
i.id as inquiry_id,
i.body as master_body,
h.body as history_body,
q.body as question_body,
a.body as answer_body
from tbl_Inquiry_master i
left join tbl_Inquiry_history h on h.inquiry_id=i.id
left join tbl_question q on q.inquiry_id=i.id
left join tbl_answer a on a.question_id=q.id
you end up with:-
inquiry_id master_body history_body question_body answer_body
----------- -------------- --------------------------------- -------------------------- ----------------------
1 inquiry one history 1 (relates to inquiry 1) inquiry one question one answer 1 to question 1
1 inquiry one history 1 (relates to inquiry 1) inquiry one question one answer 2 to question 1
1 inquiry one history 1 (relates to inquiry 1) inquiry one question two answer 1 to question 2
2 inquiry two NULL inquiry two question one NULL
3 inquiry three history 2 (relates to inquiry 3) NULL NULL
4 inquiry four NULL inquiry four question one answer 1 to question 4
4 inquiry four NULL inquiry four question one answer 2 to question 4
4 inquiry four NULL inquiry four question one answer 3 to question 4
You can then load a repeater with this and, by suppressing repeated text, produce something that may make sense. The output would probably be easier to handle in a hierarchy (XML, etc.) rather than a rectangular result set - but that is an entirely different question.

Ok. Lets start again.
Drop the table tbl_Question from your database and erase any memory you have of it ever existing.
Drop the table tbl_Answer from your database and erase any memory you have of it ever existing.
When a user asks a question, add a row to tbl_Inquiry_master that records the question with a NEW Id. If a user needs to ask multiple questions, add a new row to tbl_Inquiry_master to record each question (each with its own unique Id).
When a member of staff replies to a question add a row to tbl_Inquiry_History that records the answer. The Inquiry_id on this row should point back to the Id of the question it relates to in tbl_Inquiry_master. If a different member of staff wants to add a second or subsequent reply to the SAME question, add another row to tbl_Inquiry_History that records their answer with the Inquiry_id on this row pointing back to the SAME Id of the question in tbl_Inquiry_master.

Related

How can i create auto increment column to my existing column?

I am working with Oracle 12c in which I have below table structure:-
CREATE TABLE patients (
patient_id Integer NOT NULL,
customer_id Integer NOT NULL,
title varchar(5) NOT NULL,
fname varchar(125) NOT NULL,
lname varchar(125) NOT NULL,
dob date NOT NULL,
is_medical_card NUMBER(1) NOT NULL CHECK (is_medical_card IN (0,1)),
scheme_number Integer NOT NULL,
status varchar(50) NOT NULL,
created_on date NOT NULL,
last_update_date date NOT NULL,
consent_flag NUMBER(1) NOT NULL CHECK (consent_flag IN (0,1)),
relationship varchar(50) NOT NULL
);
Where patient_id is my primary key so now I want to make it auto increment as well so please let me how can I do this so make it auto increment.
Thanks!
Need to create auto increment to existing column.
You might want to use Identities - Creating a table with an Identity gives you the chance to omit the ID values and let Oracle use a sequence on your desired column:
1. Let's Create the Table:
CREATE TABLE identities (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
description varchar2(100) NOT NULL
);
Table created.
2. You'll want to create a primary key to ensure uniqueness:
alter table identities add constraint id_pk primary key (ID);
Table altered.
3. Let's insert some data in different ways:
INSERT INTO identities (description)
VALUES('Insert Description omitting ID');
1 row created.
INSERT INTO identities (id,description)
VALUES(NULL,'Insert with explicit NULL value');
1 row created.
4. Save the work done
commit;
Commit complete.
5. Check the results
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
As you can see we dind't specify any number for the ID, but the Identity on the ID column did for us
Note: Mind that you can manually insert an ID, but this will mess up with the Identity as it'll normally do with a standard Sequence:
INSERT INTO identities (id,description)
VALUES(3,'Manually insert an ID value');
1 row created.
INSERT INTO identities (description)
VALUES('Test Nextval');
INSERT INTO identities (description)
*
ERROR at line 1:
ORA-00001: unique constraint (XXX.ID_PK) violated
This error, because it tries to insert a '3' into the ID that was manually inserted with the statement before.
Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
Re-Run the "NEXTVAL" insert:
INSERT INTO identities (description)
VALUES('Test Nextval');
1 row created.
Re-Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
4 Test Nextval
Hope this Helps.

Is there pattern to have union table for different items?

I'd like to have column constraint based combination of 2 columns. I don't find the way to use foreign key here, because it should be conditional FK, then. Hope this basic SQL shows the problem:
CREATE TABLE performer_type (
id serial primary key,
type varchar
);
INSERT INTO performer_type ( id, type ) VALUES (1, 'singer'), ( 2, 'band');
CREATE TABLE singer (
id serial primary key,
name varchar
);
INSERT INTO singer ( id, name ) VALUES (1, 'Robert');
CREATE TABLE band (
id serial primary key,
name varchar
);
INSERT INTO band ( id, name ) VALUES (1, 'Animates'), ( 2, 'Zed Leppelin');
CREATE TABLE gig (
id serial primary key,
performer_type_id int default null, /* FK, no problem */
performer_id int default null /* want FK based on previous FK, no good solution so far */
);
INSERT INTO gig ( performer_type_id, performer_id ) VALUES ( 1,1 ), (2,1), (2,2), (1,2), (2,3);
Now, the last INSERT works, but for last 2 value pairs I'd like it fail, because there is no singer ID 2 nor band ID 3. How to set such constraint?
I already asked similar question in Mysql context and only solution was to use trigger. Problem with trigger was: you can't have dynamic list of types and table set. I'd like to add types (and related tables) on the fly.
I also found very promising pattern, but this is upside down for me, I did not figured out, how to turn it to work in my case.
What I am looking here seems to me so useful pattern, I think there must be some common way for it. Is it?
Edit.
Seems, I choose bad items in my examples, so I try make it clear: different performer tables (singer and band) have NO relation between them. gig-table just has to list tasks for different performers, without setting any relations between them.
Another example would items in stock: I may have item_type-table, which defines hundreds of item-types with related tables (for example, orange and house), and there should be table stock which enlists all appearances of items.
PostgreSQL I use is 9.6
Based on #Laurenz Albe answer I form a solution for example above. Main difference: there is parent table performer, which PK is FK/PK for specific performer-tables and is referenced also from gig table.
CREATE TABLE performer_type (
id serial primary key,
type varchar
);
INSERT INTO performer_type ( id, type ) VALUES (1, 'singer' ), ( 2, 'band' );
CREATE TABLE performer (
id serial primary key,
performer_type_id int REFERENCES performer_type(id)
);
CREATE TABLE singer (
id int primary key REFERENCES performer(id),
name varchar
);
INSERT INTO performer ( performer_type_id ) VALUES (1); -- get PK 1 for next statement
INSERT INTO singer ( id, name ) VALUES (1, 'Robert');
CREATE TABLE band (
id int primary key REFERENCES performer(id),
name varchar
);
INSERT INTO performer ( performer_type_id ) VALUES (2); -- get PK 2 for next statement
INSERT INTO singer ( id, name ) VALUES (2, 'Animates');
INSERT INTO performer ( performer_type_id ) VALUES (2); -- get PK 3 for next statement
INSERT INTO singer ( id, name ) VALUES (3, 'Zed Leppelin');
CREATE TABLE gig (
id serial primary key,
performer_id int REFERENCES performer(id)
);
INSERT INTO gig ( performer_id ) VALUES (1), (2), (3), (4);
And the last INSERT fails, as expected:
ERROR: insert or update on table "gig" violates foreign key constraint "gig_performer_id_fkey"
DETAIL: Key (performer_id)=(4) is not present in table "performer".
But
For me there is annoying problem: I have no good way to make distinction which ID is for singer and which for band etc. (in original example I had performer_type_id in gig-table for that), because any performer_id may belong any performer. So I'd like any performer type has it's own ID range, so I create dummy table for every sequence
CREATE TABLE band_id (
id int primary key,
dummy boolean default null
);
CREATE SEQUENCE band_id_seq START 1;
ALTER TABLE band_id ALTER COLUMN id SET DEFAULT nextval('band_id_seq');
CREATE TABLE singer_id (
id int primary key,
dummy boolean default null
);
CREATE SEQUENCE singer_id_seq START 2000000;
ALTER TABLE singer_id ALTER COLUMN id SET DEFAULT nextval('singer_id_seq');
Now, to insert new row into specific perfomer table I have to get next ID for it:
INSERT INTO band_id (dummy) VALUES (NULL);
Trying to figure out, is it possible to solve this process on DB level, or has something to done in App-level. It would be nice, if inserting into band table could:
before trigger inserting into band_id to genereate specific ID
before trigger inserting this new ID into performer-table
include this new ID into INSERT into band
Frist 2 points are easy, but the last point is not clear for now.

sql drop primary key from temp table

I want to create e temp table using select into syntax. Like:
select top 0 * into #AffectedRecord from MyTable
Mytable has a primary key. When I insert record using merge into syntax primary key be a problem. How could I drop pk constraint from temp table
The "SELECT TOP (0) INTO.." trick is clever but my recommendation is to script out the table yourself for reasons just like this. SELECT INTO when you're actually bringing in data, on the other hand, is often faster than creating the table and doing the insert. Especially on 2014+ systems.
The existence of a primary key has nothing to do with your problem. Key Constraints and indexes don't get created when using SELECT INTO from another table, the data type and NULLability does. Consider the following code and note my comments:
USE tempdb -- a good place for testing on non-prod servers.
GO
IF OBJECT_ID('dbo.t1') IS NOT NULL DROP TABLE dbo.t1;
IF OBJECT_ID('dbo.t2') IS NOT NULL DROP TABLE dbo.t2;
GO
CREATE TABLE dbo.t1
(
id int identity primary key clustered,
col1 varchar(10) NOT NULL,
col2 int NULL
);
GO
INSERT dbo.t1(col1) VALUES ('a'),('b');
SELECT TOP (0)
id, -- this create the column including the identity but NOT the primary key
CAST(id AS int) AS id2, -- this will create the column but it will be nullable. No identity
ISNULL(CAST(id AS int),0) AS id3, -- this this create the column and make it nullable. No identity.
col1,
col2
INTO dbo.t2
FROM t1;
Here's the (cleaned up for brevity) DDL for the new table I created:
-- New table
CREATE TABLE dbo.t2
(
id int IDENTITY(1,1) NOT NULL,
id2 int NULL,
id3 int NOT NULL,
col1 varchar(10) NOT NULL,
col2 int NULL
);
Notice that the primary key is gone. When I brought in id as-is it kept the identity. Casting the id column as an int (even though it already is an int) is how I got rid of the identity insert. Adding an ISNULL is how to make a column nullable.
By default, identity insert is set to off here to this query will fail:
INSERT dbo.t2 (id, id3, col1) VALUES (1, 1, 'x');
Msg 544, Level 16, State 1, Line 39
Cannot insert explicit value for identity column in table 't2' when IDENTITY_INSERT is set to OFF.
Setting identity insert on will fix the problem:
SET IDENTITY_INSERT dbo.t2 ON;
INSERT dbo.t2 (id, id3, col1) VALUES (1, 1, 'x');
But now you MUST provide a value for that column. Note the error here:
INSERT dbo.t2 (id3, col1) VALUES (1, 'x');
Msg 545, Level 16, State 1, Line 51
Explicit value must be specified for identity column in table 't2' either when IDENTITY_INSERT is set to ON
Hopefully this helps.
On a side-note: this is a good way to play around with and understand how select insert works. I used a perm table because it's easier to find.

Insert data into strongly normalized DB and maintain the integrity (Postgres)

I'm trying to develop a simple database for the phonebook. This is what I wrote:
CREATE TABLE phone
(
phone_id SERIAL PRIMARY KEY,
phone CHAR(15),
sub_id INT, -- subscriber id --
cat_id INT -- category id --
);
CREATE TABLE category
(
cat_id SERIAL PRIMARY KEY, -- category id --
cat_name CHAR(15) -- category name --
);
CREATE TABLE subscriber
(
sub_id SERIAL PRIMARY KEY,
name CHAR(20),
fname CHAR(20), -- first name --
lname CHAR(20), -- last name --
);
CREATE TABLE address
(
addr_id SERIAL PRIMARY KEY,
country CHAR(20),
city CHAR(20),
street CHAR(20),
house_num INT,
apartment_num INT
);
-- many-to-many relation --
CREATE TABLE sub_link
(
sub_id INT REFERENCES subscriber(sub_id),
addr_id INT
);
I created a link table for many-to-many relation because few people can live at the same address and one person can live in different locations at different times.
But I cannot figure out how to add data in strongly normalized DB like this and maintain the integrity of the data.
The first improvement was that I added inique key on address table bacause this table should not contain duplicated data:
CREATE TABLE address
(
addr_id SERIAL PRIMARY KEY,
country CHAR(20),
city CHAR(20),
street CHAR(20),
house_num INT,
apartment_num INT,
UNIQUE (country, city, street, house_num, apartment_num)
);
Now the problem is how to add a new record about some person into DB. I think I should use the next order of actions:
Insert a record into subscriber table, because sub_link and phone tables must use id of a new subscriber.
Insert a record into address table because addr_id must exist before adding record into sub_link.
Link last records from subscriber and address in sub_link table. But at this step I have a new problem: how can I get sub_id and addr_id from steps 1) and 2) in PostgreSQL effectively?
Then I need to insert a record into the phone table. As at 3) step I dont know how to get sub_id from previous queries effectively.
I read about WITH block in the Postgres but I cannot figure out how to use it in my case.
UPDATE
I've done like ASL suggested:
-- First record --
WITH t0 AS (
WITH t1 AS (
INSERT INTO subscriber
VALUES(DEFAULT, 'Twilight Sparkle', NULL, NULL)
RETURNING sub_id
),
t2 AS (
INSERT INTO address
VALUES(DEFAULT, 'Equestria', 'Ponyville', NULL, NULL, NULL)
RETURNING addr_id
)
INSERT INTO sub_link
VALUES((SELECT sub_id FROM t1), (SELECT addr_id FROM t2))
)
INSERT INTO phone
VALUES (DEFAULT, '000000', (SELECT sub_id FROM t1), 1);
But I have an error: WITH clause containing a data-modifying statement must be at the top level
LINE 2: WITH t1 AS (INSERT INTO subscriber VALUES(DEFAULT,
You can do it all in one query using a WITH block with a RETURNING clause. See PostgreSQL docs on INSERT. For example:
WITH t1 AS (INSERT INTO subscriber VALUES ... RETURNING sub_id),
t2 AS (INSERT INTO address VALUES ... RETURNING addr_id)
INSERT INTO sub_link VALUES ((SELECT sub_id FROM t1), (SELECT addr_id FROM t2))
Note that this simple form will only work when inserting a single row into each table.
This is somewhat off the topic of your question, but I suggest you also consider making sub_id and cat_id columns in the phone table foreign keys (use REFERENCES).
You got the idea. Insert data from topmost tables so that you have their IDs before inserting references to them.
In PostgreSQL you can use INSERT/UPDATE ... RETURNING id construct. If you are not using some ORM which do it automatically, this may be useful.
The only thing here is that in step 2 you probably want to check if the address already exists before inserting:
SELECT addr_id FROM address WHERE country = ? AND city = ? ...

How to fill table variable with correct IDENTITY values?

Well, I have two tables:
CREATE TABLE Temp(
TEMP_ID int IDENTITY(1,1) NOT NULL, ... )
CREATE TABLE TEMP1(
TEMP1_ID int IDENTITY(1,1) NOT NULL,
TEMP_ID int, ... )
they are linked with TEMP_ID foreign key.
In a stored procedure I need to create tons of
Temp and Temp1 rows and update them, so I created a table variable (#TEMP) and I am dealing with it and finally make one big INSERT into Temp. My question is: how can I fill #Temp with correct TEMP_ID's without insert safely from multiple sessions?
you can use Scope_Identity() to find out last inserted row. You can use Output clause to find all newly inserted (or updated) rows.
create table #t1
(
id int primary key identity,
val int
)
Insert into #t1 (val)
output inserted.id, inserted.val
values (10), (20), (30)