Flexible search for customer SQL - sql

Person
| p_id | n_name | l_name | address | city | state | zip |
Customer
| p_id | reward_points| balance |
Person_PhoneNum
| ppn_id | p_id | number |
Main issue is that I want to attempt making a Retrieve Stored Procedure that can search by any of Person's fields as well as phone number or p_id BUT I want it to be able to handle NULL values from the parameters. Here is the Stored Procedure below:
CREATE PROCEDURE RetrieveCust(
#p_id AS varchar(50),
#f_name AS varchar(50),
#l_name AS varchar(50),
#address AS varchar(50),
#city AS varchar(50),
#state AS varchar(50),
#zip AS varchar(50),
#number AS varchar(50))
AS
BEGIN
END
I understand that I need to join the tables in order to match results but I don't know what I could do to handle NULL values. Any help would be amazing!

Any NULL in a parameter should match any value in the tables. Whenever you compare a parameter to a table field OR that comparison with a test for a null parameter:
( #f_name = f_name ) or ( #f_name is null )
then AND all those comparisons together to make up your retrieval.
The comparison against the phone number when phone number is null will result in more that one row if they have more than one phone number so Select DISTINCT on p_id.
What does Customer have to do with the query? You're not selecting on any field in that table and you don't appear to be returning any values from the procedure.

your where statement could be something like this
where (f_name = #f_name or #f_name is null)

Related

How to update a column supposed to be only numbers

I want to transform the values of a column in my table which is supposed to contain only numbers. It is the "TEL" column.
This column currently has special characters and spaces. I need to filter this.
The additional constraint is that if the very first character (only the first character) is a "+" I would have to transform the + into "00"
You will find below an example of what is expected.
Could you please help me to create such a query?
CREATE TABLE PersonsInitial (
tel varchar(255),
firstname varchar(255),
lastname varchar(255)
);
insert into PersonsInitial(tel,firstname,lastname) values
('+41/ jfakl2 eaf3efa54844','Manu','Johns'),
('01-afe fa-e8fa5a +e5+ e+234','Fernand','Wajk'),
(' +41/34 jfakl2 eaf3efa54844','Fred','Johns')
;
select tel, firstname, lastname from PersonsInitial
--if there is a person with the same tel number chose the customer id with 'C'
--if I don't have the choice add the customer without C
CREATE TABLE PersonsFinal (
tel varchar(255),
firstname varchar(255),
lastname varchar(255))
;
insert into PersonsFinal(tel,firstname,lastname) values
('00412354844','Manu','Johns'),
('01855234','Fernand','Wajk'),
('0041342354844','Fred','Johns')
;
select tel, firstname, lastname from PersonsFinal
Example
You can add a User-Defined Function for this.
For example:
CREATE FUNCTION dbo.fnCleanTel (#Input VARCHAR(255))
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #Output VARCHAR(255) = LTRIM(RTRIM(#Input));
IF LEFT(#Output, 1) = '+'
SET #Output = STUFF(#Output,1,1,'00');
WHILE PATINDEX('%[^0-9]%', #Output) > 0
SET #Output = REPLACE(#Output, SUBSTRING(#Output,PATINDEX('%[^0-9]%',#Output),1),'');
RETURN #Output;
END;
CREATE TABLE PersonsFinal (
tel varchar(255),
firstname varchar(255),
lastname varchar(255)
);
insert into PersonsFinal (tel, firstname, lastname)
select dbo.fnCleanTel(tel) as clean_tel, firstname, lastname
from PersonsInitial;
select tel, firstname, lastname from PersonsFinal
tel | firstname | lastname
:------------ | :-------- | :-------
00412354844 | Manu | Johns
01855234 | Fernand | Wajk
0041342354844 | Fred | Johns
db<>fiddle here
This is something that translate is useful for. You can define a string of characters to remove and replace them all with a single character also to be removed, eg a space, with some additional logic for your initial 00 criteria:
declare #replace varchar(30)='abcdefghijklmnopqrstuvwxyz/+-';
select *,
Replace(Translate(Iif(Left(Replace(tel,' ',''),1)='+',Concat('00',tel),tel), #replace, Replicate(' ',Len(#replace))),' ','')
from PersonsInitial;
You can then use an Updatable CTE to fix the original data, see example in Amended DBFiddle
I'd suggest once you have fixed your data you use a Check Constraint to ensure only valid data can be used.

Threshold SQL Table

I have a screenshot of a GUI and need to make it into a SQL table. What would be the best way of going about this?
There are 3 distinct entities in that screenshot, so define 3 tables.
Create Table ProbabilityThresholds(
Name varchar (50),
MinValue int,
MaxValue int
)
Create Table RisksMatrix (
Probability varchar(50),
MinorSeverity varchar(50),
ModerateSeverity varchar(50),
MajorSeverity varchar(50),
ExtremeSeverity varchar(50),
SortOrder int
)
Create Table RiskToCategory (
Risk varchar(50),
Category varchar(50),
SortOrder int
)
Then Query the 3 tables in multiple result sets (either as 3 direct queries or as a stored procedure). The as expression sets a column alias, and the SortOrder column is populated to sort in the desired layout, but is not selected in the results:
Select Name
, MinValue
, MaxValue
from ProbabilityThresholds
order by MinValue;
Select
Probability
,MinorSeverity as "Minor Harm",
,ModerateSeverity as "Moderate Harm",
,MajorSeverity as "Major Harm",
,ExtremeSeverity as "Extreme Harm",
from RisksMatrix
order by SortOrder ;
Select
Risk
,Category
from RiskToCategory
order by SortOrder;

Can I use OUTPUT INTO to add data to a relational table with additional values?

I have two tables. One holds common data for articles, and the other holds translations for text. Something like this:
Articles Table
id | key | date
Translations Table
id | article_key | lang | title | content
key is a string and is the primary key.
article_key is a foreign key relating it to articles on the key column.
When I add a new row to the Articles, I'd like to be able to use the key that was just inserted and add a new row to the Translations Table.
I've read about OUTPUT INTO but it doesn't seem like I can add other values to the Translations table. Also I get an error about it being on either side of a relationship.
Is my only course of action to INSERT into Articles followed by an INSERT with a SELECT subquery to get the key?
Edit: Expected output would be something like:
Articles
id | key | date
---------------
1 | somekey | 2018-05-31
Article Translations
id | article_key | lang | title | content
-----------------------------------------
1 | somekey | en | lorem | ipsum
Well this could work based on your description:
SET NOCOUNT ON;
DECLARE #Articles TABLE (id INT NOT NULL
, [key] VARCHAR(50) NOT NULL
, [date] DATE NOT NULL);
DECLARE #ArticleTranslations TABLE (id INT NOT NULL
, article_key VARCHAR(50) NOT NULL
, lang VARCHAR(50) NOT NULL
, title VARCHAR(50) NOT NULL
, content VARCHAR(50) NOT NULL);
INSERT #Articles (id, [key], [date]) -- This is insert into #Articles
OUTPUT INSERTED.id, INSERTED.[key], 'en', 'lorem', 'ipsum' -- This is insert into #ArticleTranslations
INTO #ArticleTranslations (id, article_key, lang, title, content) -- This is insert into #ArticleTranslations
VALUES (1, 'somekey', GETDATE()); -- This is insert into #Articles
SELECT *
FROM #Articles;
SELECT *
FROM #ArticleTranslations;
Try this out Stack Exchange: https://data.stackexchange.com/stackoverflow/query/857925
Maybe it's not that simple as it is. So let me know whether this works or not.

SQL Merge n rows from source table to two targets

We have an old database that we maintain, and a new one that we have started using. We need to periodically transfer data from the old db to the new one. At the moment, we need to transfer, or merge as it might also be called, data from one table - Student, in the old database to two tables (ie two targets) in the new one - Person and Student. Now the catch is that the data from the old, source, database should be divided among the two tables in the new one. For example (just for the sake of this post),
Old table 'Student'
------------------------------
IdNo | FirstName | LastName |
578 | John | Doe |
645 | Sara | Doe |
New table 'Person'
-----------
Id | IdNo |
11 | 578 |
23 | 645 |
New table 'Student'
--------------------------------------
Id | PersonId | FirstName | LastName |
101| 11 | John | Doe |
102| 23 | Sara | Doe |
And the procedure should take a parameter of the number of rows to merge.
How can this be accomplished?
Update
Perhaps it would be easier for you guys to know what I mean by pseudo code:
MERGE [NewDB].[dbo].[Person] p, [NewDB].[dbo].[Student] ns -- 2 targets, this does not work
USING [OldDB].[dbo].[student] os -- source table, old student
ON p.IdNo = s.IdNo
WHEN MATCHED THEN -- Update existing rows
UPDATE p
SET p.SomeCoumn1 = os.SomeColumn1 -- works. os (old student) is know here
UPDATE ns
SET ns.SomeColumn2 = os.SomeColumn2 -- Does not work. os is not known here
WHEN NOT MATCHED BY TARGET THEN -- Add new rows
INSERT INTO p (IdNo, SomeOlumn1)
VALUES (os.Idno, os.SomeColumn1); -- os (old Studnet) is known here
INSERT INTO ns (SomeColumn2)
VALUES (os.SomeColumn2); -- Does not work. os is not knwon here
I hope that makes it somewhat clearer.
May we assume the reason you want to do this in one statement instead of two is that one if the fields in the first table you are inserting in to is an identity field (Id in the Person table in your example) that needs to be inserted into the second table?
If so, add an OUTPUT clause in the first merge statement so that you have the relationship and fields you require for the second merge statement.
declare #OldStudent table (IdNo int, FirstName varchar(30), LastName varchar(30))
declare #Person table (Id int identity, IdNo int)
declare #NewStudent table (Id int identity, PersonId int, FirstName varchar(30), LastName varchar(30))
insert #OldStudent (IdNo, FirstName, LastName)
select 578, 'John', 'Doe'
union all select 645, 'Sara', 'Doe'
declare #output table ([Action] varchar(20), PersonId int, IdNo int)
MERGE #Person p
USING #OldStudent os
ON p.IdNo = os.IdNo
WHEN MATCHED THEN -- Update existing rows
UPDATE SET IdNo = os.IdNo
WHEN NOT MATCHED BY TARGET THEN -- Add new rows
INSERT (IdNo) VALUES (os.Idno)
OUTPUT $action, inserted.Id, inserted.IdNo into #output;
WITH src AS
(
select
o.IdNo, o.PersonId, os.FirstName, os.LastName
from
#output o
inner join #OldStudent os on os.IdNo = o.IdNo
)
MERGE INTO #NewStudent as ns
USING src
ON src.PersonID = ns.PersonID
WHEN MATCHED THEN
UPDATE SET FirstName = src.FirstName, LastName = src.LastName
WHEN NOT MATCHED BY TARGET THEN -- Add new rows
INSERT (PersonID, FirstName, LastName) VALUES (src.PersonID, src.FirstName, src.LastName);
select * from #Person
select * from #NewStudent

Splitting sql pipe separated

I have table as follows
Discese
ID | DisceseNAme
1 | Heart
2 | Lungs
3 | ENT
Registration
PatienID | NAME | Discease
1 | abc | 1
2 | asa | 2|3
3 | asd | 1|2|3
I have a function to split |-separated data. Now I want result as:
PatientID | Name | DisceseNAme
1 | abc | heart
2 |asa | Lungs,ENT
3 |asd | heart,Lungs,ENT
My split function is
ALTER FUNCTION [dbo].[fnSplit](
#sInputList VARCHAR(8000) -- List of delimited items
, #sDelimiter VARCHAR(8000) = '|' -- delimiter that separates items
) RETURNS #List TABLE (item VARCHAR(8000))
BEGIN
DECLARE #sItem VARCHAR(8000)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList -- Put the last item in
RETURN
END
I am not sure how I can get that result, though.
As already mentioned in the comments, it is better to normalize your table structure. What this means is that you should not store patient's diseases in one VARCHAR column with disease ID's separated with some character. Instead you should store all diseases for a patient in separate rows.
If you keep using the setup you have now, your queries will become real cumbersome and performance will be really bad. Also, you will not be able to enjoy database consistency by using foreign keys.
I've written this example script which finally selects for the output you require. The example uses temporary tables. If you choose to use this way of working (and you should), just use this setup with regular tables (ie not starting with #).
The tables:
#disease: Defines diseases
#patients: Defines patients
#registration: Defines patients' diseases; foreign keys to #disease and #patients for data consistency (make sure the patients and diseases actually exist in the database)
If you're wondering how the FOR XML PATH('') construct in the final query results in a |-separated VARCHAR, read this answer I gave a while ago on this subject.
-- Diseases
CREATE TABLE #disease(
ID INT,
DiseaseName VARCHAR(256),
CONSTRAINT PK_disease PRIMARY KEY(ID)
);
INSERT INTO #disease(ID,DiseaseName)VALUES
(1,'Heart'),(2,'Lungs'),(3,'ENT');
-- Patients
CREATE TABLE #patients(
PatientID INT,
Name VARCHAR(256),
CONSTRAINT PK_patients PRIMARY KEY(PatientID)
);
INSERT INTO #patients(PatientID,Name)VALUES
(1,'abc'),(2,'asa'),(3,'asd'),(4,'zldkzld');
-- Registration for patient's diseases
CREATE TABLE #registration(
PatientID INT,
Disease INT,
CONSTRAINT FK_registration_to_patient FOREIGN KEY(PatientID) REFERENCES #patients(PatientID),
CONSTRAINT FK_registration_to_disease FOREIGN KEY(Disease) REFERENCES #disease(ID),
);
INSERT INTO #registration(PatientID,Disease)VALUES
(1,1), -- patient with ID 1 has one disease: Heart
(2,2),(2,3), -- patient with ID 2 has two diseases: Lungs and ENT
(3,1),(3,2),(3,3); -- patient with ID 3 has three diseases: Heart, Lungs and ENT
-- Select diseases for partients in one |-separated column
SELECT
p.PatientID,p.Name,Diseases=STUFF(dn.diseases,1,1,'')
FROM
#patients AS p
CROSS APPLY ( -- construct a |-separated column with all diseases for the client
SELECT
'|'+d.DiseaseName
FROM
#registration AS r
INNER JOIN #disease AS d ON
d.ID=r.Disease
WHERE
r.PatientID=p.PatientID
FOR
XML PATH('')
) AS dn(diseases)
WHERE
EXISTS(SELECT 1 FROM #registration AS r WHERE r.PatientID=p.PatientID)
ORDER BY
p.PatientID;
DROP TABLE #disease;DROP TABLE #registration;DROP TABLE #patients;
Results:
+-----------+------+-----------------+
| PatientID | Name | Diseases |
+-----------+------+-----------------+
| 1 | abc | Heart |
| 2 | asa | Lungs|ENT |
| 3 | asd | Heart|Lungs|ENT |
+-----------+------+-----------------+