Inserting multiple names in same cell with separated commas - sql

Previously, I was trying to keep ALL previous last names of an employee in a table cell with commas (see below) but I didn’t know how. Someone then suggested using normalization which I’m not sure if it’ll be easier in this situation. My boss wants to display all previous last names of an employee on a web page each time she edits her account info. Simply put, when Judy changes her last name again – her last name Kingsley should be inserted behind Smith. So, my question is back to whether or not it is possible to add multiple last names in the same cell, separated with commas as I thought it’ll be simpler when I use a variable on the web page to display all the Alias at once? Yet, I’ve no clue the complexity to write the codes for this. Any help is truly appreciated.
Current SQL table
+---------------+-----------------+----------------+--------------------+
People FirstName LastName Alias
+---------------+-----------------+----------------+--------------------+
002112 Judy Smith Hall
Preferred
+---------------+-----------------+----------------+--------------------+
People FirstName LastName Alias
+---------------+-----------------+----------------+--------------------+
002112 Judy Kingsley Hall, Smith

Keep the database normalized.
People:
(Id, Firstname, Lastname)
LastnameHistory:
(PeopleId, OldLastname, NewLastname, DateChanged)
You can the create a view which would be a "GROUP_CONCAT" type of query to transform the data as required.
An example:
DECLARE #people TABLE ( id INT IDENTITY(1,1), fname VARCHAR(50), lname VARCHAR(50))
DECLARE #lnameHistory TABLE ( id INT IDENTITY(1,1), people_id INT, lname VARCHAR(50), date_changed DATETIME)
INSERT INTO #people (fname, lname)
VALUES ('john', 'smith'), ('jane', 'doe')
INSERT INTO #lnameHistory (people_id, lname, date_changed)
VALUES (2, 'shakespeare', '2012-01-01'), (2, 'einstein', '2013-12-12')
;WITH group_concat AS
(
SELECT people_id, LEFT(lnames , LEN(lnames )-1) AS lnames
FROM #lnameHistory AS o
CROSS APPLY
(
SELECT lname + ', '
FROM #lnameHistory AS i
WHERE o.people_id = i.people_id
ORDER BY date_changed ASC
FOR XML PATH('')
) pre_trimmed (lnames)
GROUP BY people_id, lnames
)
SELECT p.*, gc.lnames FROM #people p
JOIN group_concat gc ON gc.people_id = p.id
Some reference for syntax:
SQL Server CROSS APPLY and OUTER APPLY
XML Data (SQL Server)

Assuming your update statement is a stored procedure taking in parameters of #personId and #newLastName:
EDIT
Sorry, wrong version of SQL. Have to do it the old school way!
UPDATE PeopleTable
SET Alias = Alias + ', ' + LastName,
LastName = #newLastName
WHERE
People = #personId

When you update the table for a new LastName, use something like this:
UPDATE <table> SET Alias = Alias + ', ' + LastName, LastName = <newLastName>

Related

Get unique row based on 2 columns with duplicate values

I have a table with 3 columns and 6 rows:
As you can see based on the highlighted red text, Ash and Joey have the same Last name and Street address i.e. column "Last" and column "Street" have a duplicate value. I would like to only get one of them.
Desired result would be to get rows without duplicate values on the "Last" and "Street" columns:
Where only one of Ash or Joey is retained (I just used Ash in this example, but Joey would be fine too - just need 1 or the other, not both).
Is this even possible? Any advise appreciated, thanks.
P.S. the “Street” column is actually on a different table so the picture of the graph represents 2 tables already joined.
Since you don't care which record of the duplicates survives you can give this a shot. It'll actually keep the first one alphabetically by First
DROP TABLE IF EXISTS #t;
CREATE TABLE #t (First VARCHAR(255), Last VARCHAR(255), Street VARCHAR(255));
INSERT #t SELECT 'Ash', 'Williams', '123 Main';
INSERT #t SELECT 'Ben', 'O''Shea', '456 Grand';
INSERT #t SELECT 'Claire', 'Port', '543 Jasper';
INSERT #t SELECT 'Denise', 'Stone', '543 Jasper';
INSERT #t SELECT 'Erica', 'Thomas', '789 Holt';
INSERT #t SELECT 'Joey', 'Williams', '123 Main';
WITH dupes AS (
SELECT First,
Last,
Street,
ROW_NUMBER() OVER (PARTITION BY Last, Street ORDER BY First) RowNum
FROM #t
)
SELECT First, Last, Street
FROM dupes
WHERE RowNum = 1;
On the assumption you want to retain the person with the first name alpabetically, you can use the ROW_NUMBER window function to generate a new row number for each duplicate and use that to filter out the dupes:
CREATE TABLE Peeps
(
FirstName NVARCHAR(20),
LastName NVARCHAR(20),
Street NVARCHAR(20)
)
INSERT INTO Peeps
VALUES
('Ash','Williams','123 Main'),
('Ben','O''Shea','456 grand'),
('Claire','Port','543 Jasper'),
('Denise','Stone','543 Jasper'),
('Erica','Thomas','789 Holt'),
('Joey','Williams','123 Main')
SELECT FirstName,
LastName,
Street
FROM (
SELECT FirstName,
LastName,
Street,
ROW_NUMBER () OVER (PARTITION BY LastName,Street ORDER BY FirstName) AS RowN
FROM Peeps
) a
WHERE RowN = 1
DROP TABLE Peeps

Get all results in a single row

When I take a 'SELECT *' query from a table I would like all the results to be displayed in a single 'string' format.
For example:
SELECT * FROM Employees;
Returns:
Id FirstName LastName Age
1 John Smith 30
Instead I would like to get:
Id=1,FirstName=John,LastName=Smith,Age=30
But, if I do exactly the same for the query: SELECT * FROM Cars;
I want this query to adapt and just dynamically gets the columns from the table 'Cars' and do the same with it.
Does one of this select meets your requirements ?
Declare #Employees table(Id integer, FirstName varchar(100), LastName varchar(100), Age integer)
insert into #Employees values (1, 'John', 'Smith', 30), (2, 'John', 'Doe', 23)
select CONCAT('Id=', Id, ', FirstName=', FirstName, ', LastName=', LastName, ', Age=', Age) as Employees from #Employees
select
STUFF ((
select CONCAT('; ', 'Id=', Id, ', FirstName=', FirstName, ', LastName=', LastName, ', Age=', Age) from #Employees
FOR XML PATH('')),1,2, '') as employees
OUTPUT :
Employees
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Id=1, FirstName=John, LastName=Smith, Age=30
Id=2, FirstName=John, LastName=Doe, Age=23
Employees
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Id=1, FirstName=John, LastName=Smith, Age=30; Id=2, FirstName=John, LastName=Doe, Age=23
i don't have the Employees table so i made my own in a variable, but don't mind this, you can test this code without modifying the requests
I want to add that it's code I will use in a trigger, I will be using the 'inserted' and 'deleted' tables. This makes it much harder since I'm not able to use them in a stored procedure.

select query to match multiple columns with multiple keywords

I need to write a select query that will search a single table for words taken from a user input query string like "John Doe Engineering". The string can consist of a single or multiple words. The query string will be passed into a stored procedure as a parameter. In total there area about 20 columns that need to be searched. My first thought was something like this:
SELECT *
FROM Employees
WHERE FirstName LIKE '%John%' OR FirstName LIKE '%Doe%' OR FirstName LIKE '%Engineering%'
WHERE LastName LIKE '%John%' OR LastName LIKE '%Doe2%' OR LastName LIKE '%Engineering%'
WHERE Manager LIKE '%John%' OR Manager LIKE '%Doe%'OR Manager LIKE '%Engineering%'
WHERE Department LIKE '%John%' OR Department LIKE '%Doe%'OR Department LIKE '%Engineering%'
--repeat for 16 more table columns
But I'm not sure how to best generate the query syntax based upon the user query string input. Furthermore this seems like it would be a highly inefficient query. Would it be better to look at using full text search in this case? I'm wondering what the best approach might be?
As everyone else said - Full text search is probably the best solution for this type of thing. That said, I thought it would be fun to offer a T-SQL solution.
Quick Disclaimer 1
*I would strongly encourage you not to use the solutions below - this was intended to be a fun little SQL exercise; the performance would be bad. Also - I demonstrate two very efficient ways to split a string: one using Jeff Moden's DelimitedSplit8K, the other technique using PARSENAME *
Quick Disclaimer 2
I should point out a problem with concatenating the columns into a single string as a couple people suggested -- it can lead to false positives; consider the following query:
DECLARE #search varchar(100) = 'ab';
WITH sampleData AS (SELECT fn, ln FROM (VALUES ('aa', 'bb'), ('cc', 'dd')) t(fn,ln))
SELECT *
FROM sampleData
WHERE CONCAT(fn,ln) LIKE '%'+#search+'%';
The above query will return the first record even though the "ab" does not exist in either column. For that reason you would change the WHERE (or CHARINDEX in John's example) to look like this:
WHERE CONCAT(fn, '|||', ln) LIKE '%'+#search+'%';
My Solutions
-- SAMPLE DATA
-------------------------------------------------
DECLARE #employees TABLE
(
FirstName varchar(100),
LastName varchar(100),
Manager varchar(100),
Department varchar(100)
);
INSERT #employees
SELECT *
FROM
(
VALUES
('bob', '****', 'ddd', 'sss'),
('fff', 'fred', 'obx', 'ccc'),
('Sue', 'abcd', 'ddd', 'zzz'),
('ddd', 'dcba', '123', 'fobbb')
) xx(x1, x2, x3, x4);
-- Solution #1: when #search has <= 4 "items"
-------------------------------------------------
DECLARE #search varchar(100) = 'xx bb ff zz';
SELECT e.*
--,PARSENAME(REPLACE(#search,' ','.'), N) AS matchedPattern
FROM (VALUES (1),(2),(3),(4)) t(n)
CROSS JOIN #employees e
WHERE
CHARINDEX
(
PARSENAME(REPLACE(#search,' ','.'), N),
CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department)
) > 0;
-- Solution #2: when #search has (or can have) > 4 "items"
-------------------------------------------------
-- for this you will need delimitedsplit8k: http://www.sqlservercentral.com/articles/Tally+Table/72993/
SELECT e.*
FROM dbo.delimitedsplit8k(#search, ' ')
CROSS JOIN #employees e
WHERE
CHARINDEX
(
item,
CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department)
) > 0;

Matching First and Last Name on two different tables

I am trying to match the first name varchar (50) and last name varchar(50) from table A to the first name varchar(50) and last name varchar(50) on table B. The issue is that both table contain a lot of shortened first names like the name Andrew in table A and there might be a matching record with the last name but the first name is Andy so it comes up as not a match. Is there anyway to get around this in SQL. The shortened names is a vice verse problem meaning that both Table A and Table B have some shortened names.
Here are some more examples:
This is my current code.
Select *
FROM TableA p
JOIN TableB e ON e.CompanyNumber = 1 and e.LastName like '%' + rtrim(ltrim(p.lastname)) + '%'
and e.FirstName like '%' + ltrim(rtrim(p.firstname)) + '%'
NOTE: This is the only way to match the tables together.
Create a third table that associates Long-form and short-form names.
For examle:
Long Form Short Form
Andrew Andy
Andrew Drew
David Dave
William Will
William Bill
William Billy
William Willy
Provided you use a 3rd Table to hold you Long/Short Names as so.
CREATE TABLE TableNames
([Id] int, [OfficialName] varchar(7), [Alias] varchar(7))
;
INSERT INTO TableNames
([Id], [OfficialName], [Alias])
VALUES
(1, 'Andrew', 'Andy'),
(2, 'Andrew', 'Andrew'),
(3, 'William', 'Bill'),
(4, 'William', 'William'),
(5, 'David', 'Dave'),
(6, 'David', 'David')
The following query should give you what you are looking for.
SELECT *
FROM (
SELECT TableA.Id AS T1_Id
,CompanyId AS T1_CompanyId
,FirstName AS T1_FirstName
,LastName AS T1_LastName
,TableNames.OfficialName AS OfficialName
FROM tableA
INNER JOIN tableNames ON TableA.FirstName = TableNames.Alias
) T1
,(
SELECT tableB.Id AS T2_Id
,CompanyId AS T2_CompanyId
,FirstName AS T2_FirstName
,LastName AS T2_LastName
,TableNames.OfficialName AS OfficialName
FROM tableB
INNER JOIN tableNames ON TableB.FirstName = TableNames.Alias
) T2
WHERE T1.T1_CompanyId = T2.T2_CompanyId
AND T1.OfficialName = T2.OfficialName
AND T1.T1_LastName = T2.T2_LastName
I set up my solution sqlfiddle at http://sqlfiddle.com/#!3/64514/2
I hope this helps.
Select *
<br>FROM TableA pJOIN TableB e
<br>ON e.CompanyNumber = 1
<br>and e.LastName like '%' + rtrim(ltrim(p.lastname)) + '%'
<br>OR
<br>e.FirstName like '%' + ltrim(rtrim(p.firstname)) + '%'
Now this depends how you determine it is match,
example:
TableA:
--------
Rownum FristName LastName
1 Andy Smith
2 Andy Mathew
TableB:
--------
Rownum FristName LastName
1 Logan Andy
2 Mathew Andy
Now will you consider first record from both the tables as a match
What about the second record in both the tables?
Basing on this we can even change the query

How do I populate the identity column when inserting into a table?

I have a table whose data is inserted by selecting from another table. For example,
CREATE TABLE TestTable (ID int, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO TestTable (FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
But How do I populate the ID? I tried
INSERT INTO TestTable (SCOPE_IDENTITY()+1, FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
I don't want to make ID as identity column is because I duplicate the table structure from an exsiting one which the ID is regular column.
But it doesn't work. Any idea?
This depends on the exact SQL server you're using, but since it appears you're using Microsoft SQL Server:
Simply labeling your column as ID is not enough. You'll need to make sure that the ID column is marked in SQL Server as an Identity column. This is similar to marking a column SERIAL in PostgreSQL, or AUTOINCREMENT in MySQL. Make sure you've done this first.
Assuming you've done that, simply let the database itself add the identity value by explicitly not referencing that column in your INSERT statement. Thus, something like
INSERT INTO TestTable (FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
and relying on SQL Server's underlying identity support to fill it in for you will work fine. It looks from your example like the thing you're missing is marking the ID column as an identity column in the first place.
They are right in that you should specify a autonumber column. As below:
CREATE TABLE TestTable
(
ID int NOT NULL IDENTITY (1, 1),
Firstname varchar(100) NULL,
Lastname varchar(100) NULL
)
Then when you insert, use the following:
DECLARE #MyTableVar table( ID int);
INSERT INTO TestTable
OUTPUT INSERTED.ID
INTO #MyTableVar
SELECT FirstName, LastName
FROM Person.Contact;
Then you select #MyTableVar for the identities you inserted
Is below what you are looking for?
SELECT
id + 0
,FirstName
,LastName
INTO test_table
From Person.Contact