Creating a unique ID using first 3 character of last name and a sequence number - sql-server

I have this employee table which I want every employee to have a unique ID using the 3 first letters of their name plus a sequence number in SQL Server.
I don't remember at all how to do this I haven't used SQL in a year and kinda forgot everything.
Can anyone refresh my mind on how to do this. Google has been of no help on this matter. Thanks

Firstly, I suggest that the numeric portion of your identifier be unique in and of itself, in case the employee gets married and changes their last name. The prefix can still appear to the left of it, but should not be necessary to be unique.
If you agree with this design, then you can simply use a numeric identity column on the Employee table and combine that with the last name when retrieving the data, using a computed column. I suggest you seed the identity with a value that has enough digits to keep your identifier lengths consistent, so for example to support 90,000 employees you can use a seed of 10,000 which ensures all identifiers are 8 characters long (three letters of the name plus five numeric).
Simple example:
CREATE TABLE Employee
(
EmployeeNo int IDENTITY(10000,1) PRIMARY KEY,
LastName VarChar(64),
EmployeeID AS SUBSTRING(UPPER(LastName), 1, 3) + RIGHT('0000' + CONVERT(char(5), EmployeeNo), 5)
)
INSERT Employee (LastName) VALUES ('Smith')
SELECT * FROM Employee
Results:
EmployeeNo LastName EmployeeID
10000 Smith SMI10000
For the purposes of your SQL and table design, your tables should all use EmployeeNo as foreign key, since it is compact and unique. Apply the three-letter prefix during data retrieval and only for customer-facing purposes.

#John Wu is right. However, if you don't want to rely on the Employee no then you use NewID() function, which will create a unique number always. Below is the code.
CREATE TABLE EmployeeDetails
(
EmployeeCode int IDENTITY(1,1),
FirstName varchar(50),
LastName Varchar(50),
Empid as left(Lastname,3) + convert(varchar(500), newid())
)
INSERT EmployeeDetails VALUES ('Atul', 'Jain')
`

Related

Filtering SQL rows based on certain alphabets combination

I have a column that store user input text field from a frontend website. User can input any kind of text in it, but they will also put in a specific alphabets combination to represent a job type - for example 'dri'. As an example:
Row 1: P49384; Open vehicle bonnet-BO-dri 22/10
Row 2: P93818; Vehicle exhaust-BO 10/20
Row 3: P1933; battery dri-pu-103/2
Row 4: P3193; screwdriver-pu 423
Row 5: X939; seats bo
Row 6: P9381-vehicle-pu-bo dri
In this case, I will like to filter only rows that contain dri. From the example, you can see the text can be in any order (user behaviour, they will key whatever they like without following any kind of format). But the constant is that for a particular job type, they will put in dri.
I know that I can simply use LIKE in SQL Server to get these rows. Unfortunately, row 4 is included inside when I use this operator. This is because screwdriver contains dri.
Is there any way in SQL Server I can do to strictly only obtain rows that has dri job type, while excluding words like screwdriver?
I tried to use PATINDEX but it failed too - PATINDEX('%[d][r][i]%', column) > 0
Thanks in advance.
Your data is the problem here. Unfortunately even for denormalised data it doesn't appear to have a reliable/defined format, making parsing your data in a language like T-SQL next to impossible. What problems are there? Based on the original sample data, at a glance the following problems exist:
The first data value's delimiter isn't consistent. Rows 1-5 use a semicolon (;), but row 6 uses a hyphen (-)
The last data value's delimiter isn't consistent. Row 1, 2 & 4 use a space ( ), but row 3 uses a hyphen (-).
Internal data doesn't use a consistent delimiter. For example:
Row 1 has a the value Open vehicle bonnet-BO-dri, which appears to be the values Open vehicle bonnet, BO and dri; so the hyphen(-) is the delimiter.
Row 5 has seats bo, which appears to be the values seats and bo, so uses a space ( ) as a delimiter.
The fact that row 6 has vehicle as its own value (vehicle-pu-bo-dri), however, implies that Open vehicle bonnet and Vehicle Exhaust (on rows 1 and 2 respectively) could actually be the values Open, vehicle, & bonnet and Vehicle & Exhaust respectively.
Honestly, the solution is to fix your design. As such, your tables should likely look something like this:
CREATE TABLE dbo.Job (JobID varchar(6) CONSTRAINT PK_JobID PRIMARY KEY NONCLUSTERED, --NONCLUSTERED Because it's not always ascending
YourNumericalLikeValue varchar(5) NULL); --Obviously use a better name
CREATE TABLE dbo.JobTypeCompleted(JobTypeID int IDENTITY (1,1) CONSTRAINT PK_JobTypeID PRIMARY KEY CLUSTERED,
JobID varchar(6) NOT NULL CONSTRAINT FK_JobType_Job FOREIGN KEY REFERENCES dbo.Job (JobID),
JobType varchar(30) NOT NULL); --Must likely this'll actually be a foreign key to an actual job type table
GO
Then, for a couple of your rows, the data would be inserted like so:
INSERT INTO dbo.Job (JobID, YourNumericalLikeValue)
VALUES('P49384','22/10'),
('P9381',NULL);
GO
INSERT INTO dbo.JobTypeCompleted(JobID,JobType)
VALUES('P49384','Open vehicle bonnet'),
('P49384','BO'),
('P49384','dri'),
('P9381','vehicle'),
('P9381','pu'),
('P9381','bo'),
('P9381','dri');
Then you can easily get the jobs you want with a simple query:
SELECT J.JobID,
J.YourNumericalLikeValue
FROM dbo.Job J
WHERE EXISTS (SELECT 1
FROM dbo.JobTypeCompleted JTC
WHERE JTC.JobID = J.JobID
AND JTC.JobType = 'dri');
You can apply like operator in your query as column_name like '%-dri'. It means find out records that end with "-dri"

select a column named by another column in another table

I am implementing a database that will back a role playing game. There are two relevant tables: character and weapon (plus a third table representing a standard many-to-many relationship; plus the level of each specific instance of a weapon). A character has multiple attributes (strength, agility, magic etc.) and each weapon has a base damage, a level (defined in the many-to-many association), and receives a bonus from the associated attribute of the character wielding said weapon (strength for clubs, agility for ranged weapons etc.). The effectiveness of a weapon must be derived from the three tables. The catch is that which column of the character table applies is dependent on the specific weapon being used.
My current paradigm is to perform two select queries, one to retrieve the name of the associated attribute (varchar) from the weapon table and then one - with the previously returned value substituted in - for the value of that attribute from the wielding character. I would like to replace this with a pure sql solution.
I have searched around the nets and found two other questions:
Pivot on Multiple Columns using Tablefunc and PostgreSQL Crosstab Query but neither does quite what I'm looking for. I also found the postgres internal datatype oid [https://www.postgresql.org/docs/9.1/static/datatype-oid.html ], and was able to locate the oid of a specific column, but could not find the syntax for querying the value of the column with that oid.
Table schemeta:
create table character (
id int primary key,
agility int,
strength int,
magic int,
...);
create table weapon (
id int primary key,
damage int,
associated_attribute varchar(32), --this can be another type if it'd help
...);
create table weapon_character_m2m (
id int primary key,
weapon int, --foreign key to weapon.id
character int, --foreign key to character.id
level int);
In my mind, this should be query-able with something like this (ideally resulting in the effective damage of each weapon currently in the player's possession.):
select m2m.level as level,
weapon.associated_attribute as base_attr_name,
character.??? as base_attr,
weapon.damage as base_damage,
base_damage * base_attr * level as effective_attr -- this is the column I care about, others are for clarity via alias
from weapon_character_m2m as m2m
join weapon on weapon.id=m2m.weapon
join character on character.id=m2m.character;
where m2m.character=$d -- stored proc parameter or the like
Most online resources I've found end up suggesting the database be redesigned. This is an option, but I really don't want to have a different table for each attribute to which a weapon might associate (in practice there are nearly 20 attributes that might be associated with weapon classes).
I have heard that this is possible in MSSQL by Foreign Key'ing into an internal system table, but I have no experience with MSSQL, let alone enough to attempt something like that (and I couldn't find a working sample on the internets). I would consider migrating to MSSQL (or any other sql engine) if anyone can provide a working example.
It sounds like you can just use the CASE statement. I know it is in MS SQL...not sure about PostgreSQL.
Something like (on my phone so just estimating the code 🙂):
Select other fields,
case weapon.associated_attribute
when 'agility' then character.agility
when 'strength' then character.strength
when ...
else 0 --unhandled associate_attribute
end as base_attr
from ...
The caveat here is that you will want your character attributes to be the same type which it looks like you do.
EDIT
I worked towards a view based on your feedback and realized that a view would use an unpivot rather than the case statement above but that you could use a function to do it using the CASE structure above. There are many flavours :-) MS SQL also has table-valued functions that you could use to return one attribute type for all characters. Here is the code I was playing with. It contains both a view and a function. You can choose which seems more appropriate.
create table character (
id int primary key,
agility int,
strength int,
magic int,
--...
);
insert character values (1,10,15,20),(2,11,12,13);
create table attribute_type (
attribute_id int primary key,
attribute_name varchar(20)
);
insert attribute_type values (1,'Agility'),(2,'Strength'),(3,'Magic');
create table weapon (
id int primary key,
damage int,
--associated_attribute varchar(32), --this can be another type if it'd help
attribute_id int
--...
);
insert weapon values (1,20,1),(2,30,2);
create table weapon_character_m2m (
id int primary key,
weapon int, --foreign key to weapon.id
character int, --foreign key to character.id
level int);
insert weapon_character_m2m values (1,1,1,4),(2,2,2,5);
go
create view vw_character_attributes
as
select c.id, a.attribute_id, c.attribute_value
from (
select id, attribute_name, attribute_value
from (select id, agility, strength, magic from character) p --pivoted data
unpivot (attribute_value for attribute_name in (agility, strength, magic)) u --unpivoted data
) c
join attribute_type a on a.attribute_name = c.attribute_name
;
go
create function fn_get_character_attribute (#character_id int, #attribute_id int)
returns int
as
begin
declare #attr int;
select #attr =
case #attribute_id
when 1 then c.agility
when 2 then c.strength
when 3 then c.magic
--when ...
else 0 --unhandled associate_attribute
end
from character c
where c.id = #character_id;
return #attr;
end
go
select * from vw_character_attributes;
select m2m.level as level,
at.attribute_name as base_attr_name,
ca.attribute_value as base_attr,
dbo.fn_get_character_attribute(m2m.id, weapon.attribute_id ) function_value,
weapon.damage as base_damage,
weapon.damage * ca.attribute_value * level as effective_attr -- this is the column I care about, others are for clarity via alias
from weapon_character_m2m as m2m
join weapon on weapon.id=m2m.weapon
join vw_character_attributes ca on ca.id=m2m.character and ca.attribute_id = weapon.attribute_id
join attribute_type at on at.attribute_id = weapon.attribute_id
--where m2m.character=$d; -- stored proc parameter or the like

Problèm with SQL select grouping

I have a small problem with a SQL Server query.
I have an issue with my view of several base tables with duplicate values, so far no problem, these duplicates are logical. By unfortunately I do not get the desired end result, I could do it by programming the front end of my application but I would prefer to do the work on the server.
I will explain the principle:
I have 30 companies which each have an employee table.
My view is a union of the 30 employee tables.
Each employee has a unique serial number, the number is the same across tables, so an employee named "John Doe" with an ID number 'S0000021' can be hired in Company A then transferred to company Q without any problems, it will retain the serial number 'S0000021'.
The difference between the data from the Employee tables A and Q will be in this example the start (hire) and release (transfer) dates entered for Company A and just the start date for company Q so the view will have 2 lines for "John Doe".
12 common fields are the following:
Serial Number (Identical in every employee table)
Social Security Number (Same in every employee table)
Start/Hire Date
Release/Transfer date (empty/null if the employee is current)
Name (Can change across companies if the person divorces)
First name
Maiden name
Last Name
Gender
Final Released
Company Code
The problem seems simple that I would not appear that the latest information of the employee, except with a group by, if it has changed name or release date, it will be displayed twice.
I tried the following different ways but they don't return what I want
I returned results both ways but I always see duplicates because my dates within companies are never identical, and their name may change.
Sorry for this Google translation.
1 --
select
vue.matricule,
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
from
vue
group by
vue.matricule,
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
2---
select
distinct(vue.matricule),
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
from
vue
I assumed the following:
there is a view (vue) that already gathers all data from each of the 30 companies
you are just looking for the latest record for each employee
If you need to also see a record for each name change we can change this.
--set up test data
declare #vue table (
matricule varchar(20),
numsecu varchar(20),
name varchar(20),
lastname varchar(20),
maidenname varchar(20),
secondname varchar(20),
genre varchar(20),
start datetime,
released datetime,
companycode varchar(20));
insert #vue values
('S0000021','123456789','John', 'Doe',null,null,'M','2015-01-01','2015-12-31','A'),
('S0000021','123456789','Johnny', 'Doe',null,null,'M','2016-01-01',null,'Q'), --new company, name change, currently employed
('S0000022','123456780','Jane', 'Doe',null,null,'M','2015-01-01','2015-12-31','A'),
('S0000022','123456780','Jane', 'Doe',null,null,'M','2016-01-01','2016-02-01','Q'); --new company, name change, terminated
select * from #vue order by matricule, start;
--get latest record for each employee
select *
from (--add row numbering
select *, row_number() over (partition by matricule order by start desc) row_num
from #vue
) vue2
where vue2.row_num = 1;

Primary key detection

I have imported the data into an database.
Patient(pt_id,pt_name,pt_type,pt_loc)
Now , I want to find which of these a primary (have unique) values.
Any way I could found is
select count(pt_id) from patient
select count(distinct pt_id) from patient
would return the same value
Any input would be great :)
Thanks!!!
A single-pass method of determining if the column contains only unique values would be:
SELECT pt_id, COUNT(pt_id) from patient
GROUP BY pt_id
HAVING COUNT(pt_id) > 1
if you get results, it's not unique. You may also want to check for nulls:
SELECT * from patient
WHERE pt_id is NULL

T-SQL for Updating Rows with same value in a column

I have a table lets say called FavoriteFruits that has NAME, FRUIT, and GUID for columns. The table is already populated with names and fruits. So lets say:
NAME FRUIT GUID
John Apple NULL
John Orange NULL
John Grapes NULL
Peter Canteloupe NULL
Peter Grapefruit NULL
Ok, now I want to update the GUID column with a new GUID (using NEWID()), but I want to have the same GUID per distinct name. So I want all the John Smiths to have the same GUID, and I want both the Peters to have the same GUID, but that GUID different than the one used for the Johns. So now it would look something like this:
NAME FRUIT GUID
John Apple f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
John Orange f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
John Grapes f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
Peter Canteloupe e3b1851c-1927-491a-803e-6b3bce9bf223
Peter Grapefruit e3b1851c-1927-491a-803e-6b3bce9bf223
Can I do that in an update statement without having to use a cursor? If so can you please give an example?
Thanks guys...
Update a CTE won't work because it'll evaluate per row. A table variable would work:
You should be able to use a table variable as a source from which to update the data. This is untested, but it'll look something like:
DECLARE #n TABLE (Name varchar(10), Guid uniqueidentifier);
INSERT #n
SELECT Name, newid() AS Guid
FROM FavoriteFruits
GROUP BY Name;
UPDATE f
SET f.Guid = n.Guid
FROM #n n
JOIN FavoriteFruits f ON f.Name = n.Name
So that populates a variable with a GUID per name, then joins it back to the original table and updates accordingly.
To clarify comments re a table expression in the USING clause of a MERGE statement.
The following won't work because it'll evaluate per row:
MERGE INTO FavoriteFruits
USING (
SELECT NAME, NEWID() AS GUID
FROM FavoriteFruits
GROUP
BY NAME
) AS source
ON source.NAME = FavoriteFruits.NAME
WHEN MATCHED THEN
UPDATE
SET GUID = source.GUID;
But the following, using a table variable, will work:
DECLARE #n TABLE
(
NAME VARCHAR(10) NOT NULL UNIQUE,
GUID UNIQUEIDENTIFIER NOT NULL UNIQUE
);
INSERT INTO #n (NAME, GUID)
SELECT NAME, NEWID()
FROM FavoriteFruits
GROUP
BY NAME;
MERGE INTO FavoriteFruits
USING #n AS source
ON source.NAME = FavoriteFruits.NAME
WHEN MATCHED THEN
UPDATE
SET GUID = source.GUID;
There's a single-statement solution too, which, however, has some limitations. The idea is to use OPENQUERY(), like this:
UPDATE FavoriteFruits
SET GUID = n.GUID
FROM (
SELECT NAME, GUID
FROM OPENQUERY(
linkedserver,
'SELECT NAME, NEWID() AS GUID FROM database.schema.FavoriteFruits GROUP BY NAME'
)
) n
WHERE FavoriteFruits.NAME = n.NAME
This solution implies that you need to create a self-pointing linked server. Another specificity is that you can't use this method on table variables nor local temporary tables (global ones would do as well as 'normal' tables).

Resources