Substring in T-SQL - sql-server

I want to Change the full name if the first letter of the first name and last name are both same
This is my code:
SELECT
Id, FName + ' ' + LName 'Full Name',
SUBSTRING(LTRIM(RTRIM(FName)), 1, 1) + '. ' + LTRIM(RTRIM(LName)) 'Short Name'
FROM
testMe
Output:
Full Name Short Name Desired Result
1 John Smith J. Smith John Smith
2 Jack Smith J. Smith Jack Smith
3 Jule Smith J. Smith Jule Smith
4 Paul Smith P. Smith P. Smith
5 Steve Mark S. Mark S. Mark
6 Ashley Howard A. Howard Ashley Howard
7 Adam Campbell A. Campbell Adam Campbell
8 Alex Campbell A. Campbell Alex Campbell
Screenshot for illustration:_

I think I know what you need. If I guessed correctly...
Try this:
DECLARE #Name TABLE (ID int, FName varchar(50), LName varchar(50))
INSERT INTO #Name VALUES
(1, 'John' , 'Smith' )
, (2, 'Jack' , 'Smith' )
, (3, 'Jule' , 'Smith' )
, (4, 'Paul' , 'Smith' )
, (5, 'Steve' , 'Mark' )
, (6, 'Ashley', 'Howard' )
, (7, 'Adam' , 'Campbell')
, (8, 'Alex' , 'Campbell')
;
WITH DupFNameFirst AS
(
SELECT
FName = MAX(FName)
, Qty = SUM(1)
FROM #Name
GROUP BY LEFT(FName, 1) + LName
)
, DupLName AS
(
SELECT
LName
, Qty = SUM(1)
FROM #Name
GROUP BY LName
)
, FinalName AS
(
SELECT
ID
, FullName = FName + ' ' + LName
, ShortName = LEFT(FName,1) + '. ' + LName
, FName
, LName
FROM #Name
)
SELECT
ID
, FullName
, ShortName
, DesiredName = CASE WHEN F.Qty = 1 OR D.Qty = 1 THEN N.ShortName ELSE N.FullName END
FROM FinalName N
LEFT JOIN DupFNameFirst F ON F.FName = N.FName
INNER JOIN DupLName D ON D.LName = N.LName
ORDER BY ID

You can count short names with a window function
select [Full Name]
, case when count(*) over(partition by Short_Name) >1 then [Full Name] else Short_Name end Short_Name
from(
SELECT
FName, FName + ' ' + LName [Full Name],
SUBSTRING(LTRIM(RTRIM(FName)), 1, 1) + '. ' + LTRIM(RTRIM(LName)) Short_Name
FROM
testMe
) t

Hard to tell exactly what you need, but maybe a CASE statement would help:
SELECT
CASE
WHEN LEFT(LTRIM(RTRIM(FName)), 1) = LEFT(LTRIM(RTRIM(LName)), 1 ) THEN SUBSTRING(LTRIM(RTRIM(FName)), 1, 1) + '. ' + LTRIM(RTRIM(LName))
ELSE FName + ' ' + LName
END As FullName
The first CASE condition is when the first letter of first name = first letter of last name. 2nd condition is when they do not match.

Related

Dynamic Operators in SQL

Let's say I have the following table tbl_Rules:
RuleID NameOperator NameValues TypeOperator TypeValue
1 NotIn John In 2
1 NotIn Alex In NULL
1 NotIn Mike In NULL
2 In Mike NotIn 2
And my source table looks like this tbl_Source:
ID Name Type Cost
1 Mike 2 100
2 Cole 2 200
3 Ken 1 300
4 Tara 1 400
5 Mike 1 500
6 Sonya 1 600
7 Ann 2 700
8 Mike 1 800
I want to be able to join these two tables and get the following result tbl_Result:
RuleID Name Type Cost
1 Cole 2 200
1 Ann 2 700
2 Mike 1 500
2 Mike 1 800
If I was writing this query manually my query would look like this:
select 1, Name, Type, Cost
from tbl_Source
Where Name not in ('John', 'Alex', 'Mike') and Type in (2)
union all
select 2, Name, Type, Cost
from tbl_Source
where Name in ('Mike') and Type not in (2)
In my current setup tbl_Rule has 500 records and tbl_Source has 500k records.
Any advice on this can be achieved is greatly appreciated. Limitations:
No CLR functions, No 2017 features (e.g. String_agg)
Update: DDL statements for the above sample can be found here: http://sqlfiddle.com/#!18/9a29f/2/0
Here's one way. I have used cross join to check the rules. There may be better ways with dynamic SQL where rules are implemented in join
declare #tbl_Rules table(
RuleID int
, NameOperator varchar(20)
, NameValues varchar(20)
, TypeOperator varchar(20)
, TypeValue int
)
insert into #tbl_Rules
values
(1, 'NotIn', 'John', 'In', 2)
, (1, 'NotIn', 'Alex', 'In', NULL)
, (1, 'NotIn', 'Mike', 'In', NULL)
, (2, 'In', 'Mike', 'NotIn', 2)
declare #tbl_Source table (
ID int
, Name varchar(20)
, Type int
, Cost int
)
insert into #tbl_Source
values
(1, 'Mike', 2, 100)
, (2, 'Cole', 2, 200)
, (3, 'Ken', 1, 300)
, (4, 'Tara', 1, 400)
, (5, 'Mike', 1, 500)
, (6, 'Sonya', 1, 600)
, (7, 'Ann', 2, 700)
, (8, 'Mike', 1, 800)
;with cte as (
select
distinct Ruleid, a.NameOperator, a.TypeOperator
, NameValues = (
select
'!' + b.NameValues
from
#tbl_Rules b
where
a.RuleID = b.RuleID
and b.NameValues is not null
for xml path('')
) + '!'
, TypeValue = (
select
concat('!', b.TypeValue)
from
#tbl_Rules b
where
a.RuleID = b.RuleID
and b.TypeValue is not null
for xml path('')
) + '!'
from
#tbl_Rules a
)
select
b.RuleID, a.Name, a.Type, a.Cost
from
#tbl_Source a
cross join cte b
where
1 = case
when b.NameOperator = 'In' and charindex('!' + a.Name + '!', b.NameValues) > 0 and b.TypeOperator = 'In' and charindex(concat('!', a.Type, '!'), b.TypeValue) > 0 then 1
when b.NameOperator = 'In' and charindex('!' + a.Name + '!', b.NameValues) > 0 and b.TypeOperator = 'Notin' and charindex(concat('!', a.Type, '!'), b.TypeValue) = 0 then 1
when b.NameOperator = 'NotIn' and charindex('!' + a.Name + '!', b.NameValues) = 0 and b.TypeOperator = 'In' and charindex(concat('!', a.Type, '!'), b.TypeValue) > 0 then 1
when b.NameOperator = 'NotIn' and charindex('!' + a.Name + '!', b.NameValues) = 0 and b.TypeOperator = 'NotIn' and charindex(concat('!', a.Type, '!'), b.TypeValue) = 0 then 1
else 0
end
Output:
RuleID Name Type Cost
---------------------------
1 Cole 2 200
1 Ann 2 700
2 Mike 1 500
2 Mike 1 800
You can try this.
SELECT MAX(R_N.RuleID) RuleID, S.Name, S.Type, S.Cost
FROM tbl_Source S
INNER JOIN tbl_Rules R_N ON
(R_N.NameValues <> S.Name and R_N.NameOperator = 'NotIn' )
OR (R_N.NameValues = S.Name and R_N.NameOperator = 'In' )
INNER JOIN tbl_Rules R_S ON
R_S.RuleID = R_N.RuleID AND
(R_S.TypeValue <> S.Type and R_S.TypeOperator = 'NotIn' )
OR (R_S.TypeValue = S.Type and R_S.TypeOperator = 'In' )
GROUP BY
S.Name, S.Type, S.Cost
HAVING
MAX(R_N.NameOperator) = MIN(R_N.NameOperator)
AND MAX(R_S.TypeOperator) = MIN(R_S.TypeOperator)
Result:
RuleID Name Type Cost
----------- -------------------- ----------- -----------
1 Ann 2 700
1 Cole 2 200
2 Mike 1 500
2 Mike 1 800

Query for Splitting a Name Field in SQL Server

I have a field [Name] which I need to break up into separate parts in a query. The field is in the format:
LastName,FirstName PossibleMiddle
Sample values:Doe,John Andrew
Smith,Jane
Goal for output:
[LastName] [FirstName] [MiddleName]
---------------- ---------------- ----------------
Doe John Andrew
Smith Jane
Here is the code I've been able to work out until now:
SELECT
LEFT([Name], CHARINDEX(',', [Name]) - 1) AS [Last Name],
SUBSTRING([Name], CHARINDEX(',', [Name]) +1, LEN([Name])) AS [First and Middle Name],
SUBSTRING([Name], CHARINDEX(',', [Name]) +1, (CHARINDEX(' ', [Name])-1)) AS [First Name]
FROM t1
This code is not right for a couple of reasons:
1. It fails because the second SUBSTRING is not correctly formulated. It assumes that there will always be a space, but there will actually only be a space if there is a middle name.
2. I don't want [First and Middle Name] as one (but I think once the first issue is resolved the rest will fall into place).
I feel like I'm missing something obvious with this but it's been a long morning of trying, searching and no solution yet. Thanks in advance for taking the time to look and respond.
using cross apply() so we do not have to repeat a function all over the place for the second part:
select
lastname = left(t.str,charindex(',',t.str)-1)
, firstname = left(x.val,charindex(' ',x.val+' '))
, middlename = nullif(right(x.val,len(x.val)-charindex(' ',x.val)),x.val)
from t
cross apply (
select val = stuff(t.str,1,charindex(',',t.str),'')
) x(val)
rextester demo: http://rextester.com/TUJNX20615
returns:
+----------+-----------+------------+
| lastname | firstname | middlename |
+----------+-----------+------------+
| Doe | John | Andrew |
| Smith | Jane | NULL |
+----------+-----------+------------+
To extend your example to include mononymous individuals:
create table t (str varchar(32))
insert into t values
('Doe,John Andrew')
,('Smith,Jane')
,('Sting') -- mononymous
select
lastname = left(t.str,charindex(',',t.str+',')-1)
, firstname = nullif(left(x.val,charindex(' ',x.val+' ')),t.str)
, middlename = nullif(right(x.val,len(x.val)-charindex(' ',x.val)),x.val)
from t
cross apply (
select val = stuff(t.str,1,charindex(',',t.str),'')
) x(val)
rextester demo: http://rextester.com/WPZXC58652
returns:
+----------+-----------+------------+
| lastname | firstname | middlename |
+----------+-----------+------------+
| Doe | John | Andrew |
| Smith | Jane | NULL |
| Sting | NULL | NULL |
+----------+-----------+------------+
I believe I have this working correctly. Basically, I used two case statements, based on whether or not there was a space in the name.
So, if there is a space in the name, then break on that for the first name. If not, take the rest of the field.
DECLARE #NAME VARCHAR(50)
SET #NAME = 'Smith,Jane' -- 'Smith,Jane Ann'
SELECT LEFT(#NAME, CHARINDEX(',', #NAME) - 1) AS LastName,
CASE
WHEN (CHARINDEX(' ', #NAME) = 0)
THEN SUBSTRING(#NAME, CHARINDEX(',', #NAME) + 1, 100)
ELSE
SUBSTRING(#NAME, CHARINDEX(',', #NAME) + 1, CHARINDEX(' ', #NAME) - CHARINDEX(',', #NAME))
END AS FirstName,
CASE
WHEN (CHARINDEX(' ', #NAME) = 0)
THEN ''
ELSE
SUBSTRING(#NAME, CHARINDEX(' ', #NAME) + 1, 100)
END AS MiddleName

T-SQL return a row if another row doesn't exist

Let's say I have this table (this is simplified, of course there are other columns):
CompanyID (int)
ContactName (varchar(50))
ContactType (char(1))
with the values:
CompanyID | ContactName | ContactType
-----------------------------------------------
1 | John Doe | A
1 | Jane Smith | B
2 | Ralph Jones | B
3 | Dick Grayson | A
What I want are all the companies where there's a ContactType='A', unless there is no ContactType='A' return the ContactType='B'. So in this example, I want:
1, John Doe (because he's a ContactType A)
2, Ralph Jones (because Company #2 doesn't have a ContactType A)
3, Dick Grayson (because he's a ContactType A)
I can't just say "A or B" because a company may have both.
Here's what I tried (and failed)
use MyFancyDatabase
drop table #TypeA
drop table #TypeB
drop table #TypeAB
create table #TypeA(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeA
Select ownerkey, ContactName, ContactType from address
where ContactType = 'A' and CancelDate is null
create table #TypeB(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeB
Select ownerkey, ContactName, ContactType from address
where ContactType = 'B' and CancelDate is null
create table #TypeAB(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeAB
select * from #TypeA
except
select * from #TypeB
I guess in English it's "A, but if there is no A, then take B."
Any suggestions?
SELECT a.OwnerKey, a.CompanyName, Case WHEN a.ContactType IS NULL THEN b.ContactType ELSE a.ContactType END AS ContactType
FROM #TypeA a
LEFT JOIN #TypeB b on a.OwnerKey = b.OwnerKey
I think this should work for you.
with SortedResults as
(
select CompanyID
, ContactName
, ContactType
, ROW_NUMBER() over (partition by CompanyID order by ContactType) as RowNum
from ThisTable
)
select *
from SortedResults
where RowNum = 1
Try this SQL
Select t1.CompanyID ,
ContactName = IIF(t1.ContactType='A',t1.ContactName,t2.ContactName)
ContactType = IIF(t1.ContactType='A','A',t2.ContactType)
FROM address as t1 left join address as t2
on t1.CompanyID = t2.CompanyID
AND t1.ContactName = t2.ContactName
AND (t1.ContactType <> t2.ContactType)
IF you have more type than A OR B and you want just A and B add this on the where statment
WHERE (t1.ContactType = 'A' OR t1.ContactType = 'B')
AND (t2.ContactType = 'A' OR t2.ContactType = 'B')
The columns in your defined table don't appear to match with the ones in your example query. I'm guessing OwnerKey is the same as CompanyID?
If so, and you keep most of the rest of your code, the last select would need to be:
select * from #TypeA
union all
select * from #TypeB
where not exists (
select *
from #TypeA
where #TypeA.ownerkey = #TypeB.ownerkey)

Create query with dynamic columns from different tables

I'm trying to create a query with dynamic columns, based on data from three tables.
This is the database structure:
STUDENT
studentID int,
studentNumber int,
studentName nvarchar(100).
EXAM:
examID int,
examName varchar(100),
examenDate datetime,
EXAM_REGISTRATION:
studentID int,
examID int,
A record is added to the EXAM_REGISTRATION table when a student has registered for an exam.
What I'm trying to get is a list of all the exams and all the students in a pivot table to see which students have registered for which exams, like this:
Quite frankly I don't know where to start.
I can query everything individually and put it all together but how can I combine it into one query?
I've been researching pivot tables, but every example seems to query only from one table and uses numbers and functions like MIN, AVG etc.
Can someone help me along?
ok lets go
some data to play with
create table #student
(studentID int, studentNumber int, studentName nvarchar(100))
create table #exam
(examID int, examName nvarchar(100), examDate datetime)
create table #examReg
(studentID int, examID int)
insert into #student
values (1, 787878, 'pierwszy')
,(2, 89898, 'drugi')
,(3, 343434, 'trzeci')
,(4, 121212, 'czwarty')
insert into #exam
values (1, 'exPierwszy', GETDATE())
,(2, 'exDrugi', GETDATE())
,(3, 'exTrzeci', GETDATE())
insert into #examReg
values (1,2),(1,3)
, (2,2),(2,3)
,(3,1),(3,2)
,(4,1),(4,2),(4,3)
and now the main part, and explanation
first of all you have to get pivot query
select examName, examDate , min([1]) , min([2]), min([3]) ,min([4])--studentID as studentID, examDate --,studentNumber
from
(select a.studentID , studentNumber, examDate, examName
from #student a
join #examReg b on a.studentID = b.studentID
join #exam c on c.examID = b.examID ) as m
pivot
(min(studentNumber) FOR studentID in ([1],[2],[3],[4])) as t
group by examName, examDate
as you have it , just change it select statement and studentID list in pivot declaration, you have to generate those parts dynamicly , so we just copy previously written query and replace columns with our token
declare #sqlTemplate nvarchar(max) =
'select examName, examDate ##sqlColumnList##
from
(select a.studentID , studentNumber, examDate, examName
from #student a
join #examReg b on a.studentID = b.studentID
join #exam c on c.examID = b.examID ) as m
pivot
(min(studentNumber) FOR studentID in (##sqlStudentIDList##)) as t
group by examName, examDate
'
after that you generate column list and studentID list by concatenting strings in tsql
declare #sqlColumnList nvarchar(max) = ''
select #sqlColumnList += ',min([' + cast(studentID as nvarchar(10)) + ']) as [' + studentName +'(' + cast(studentNumber as nvarchar(10)) + ')]'
from #student
declare #sqlStudentIDList nvarchar(max) = ''
select #sqlStudentIDList += '[' + CAST(studentID as nvarchar(10)) + '],'
from #student
set #sqlStudentIDList = SUBSTRING(#sqlStudentIDList, 0, LEN(#sqlStudentIDList))
select #sqlStudentIDList
once you have it , all you have to do is to replace tokens in previous template
set #sqlTemplate = REPLACE(#sqlTemplate, '##sqlColumnList##', #sqlColumnList)
set #sqlTemplate = REPLACE(#sqlTemplate, '##sqlStudentIDList##', #sqlStudentIDList)
select #sqlTemplate
exec sp_executesql #sqlTemplate
and thats it
if you want to read more about pivot go for msdn
if you want to read about dynamic go for this link
edit: to adjust the query for the question from comment you would have to change #sqlColumnList like that
select #sqlColumnList += ',min(' + QUOTENAME(studentID) + ') as Student' + CAST(studentID as nvarchar(10)) + '_REG,
'''+ studentName + ''' as Student' + cast(studentID as nvarchar(10)) + '_NAME,
'+ cast(studentID as nvarchar(10)) + ' as Student' + cast(studentID as nvarchar(10)) + '_ID'
from #student
This is a pivot of the data. I would perform this slightly different than the other answer. If you know all of the values, then you can hard-code the values.
A static version will be:
select examname,
examendate,
IsNull([Smith, John (14323)], 'false') [Smith, John (14323)],
IsNull([Craft, Peter (14334)], 'false') [Craft, Peter (14334)],
IsNull([Davis, Alan (13432)], 'false') [Davis, Alan (13432)],
IsNull([Newman, Ted (133123)], 'false') [Newman, Ted (133123)]
from
(
select e.examname,
e.examenDate,
s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')' studentNameNum,
'true ' as Flag
from exam e
left join exam_registration er
on e.examid = er.examid
right join student s
on er.studentid = s.studentid
) src
pivot
(
max(flag)
for studentNameNum in ([Smith, John (14323)], [Craft, Peter (14334)],
[Davis, Alan (13432)], [Newman, Ted (133123)])
) piv
See SQL Fiddle with Demo
If your values are unknown then the query will be:
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')')
from student s
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsNull = STUFF((SELECT distinct ',IsNull(' + QUOTENAME(s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')')+', ''false'')'+' as '+QUOTENAME(s.studentName+' ('+cast(s.studentnumber as varchar(50))+')')
from student s
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT examname,
examenDate,' + #colsNull + ' from
(
select e.examname,
e.examenDate,
s.studentName + '' (''+cast(s.studentnumber as varchar(50))+'')'' studentNameNum,
''true '' as Flag
from exam e
left join exam_registration er
on e.examid = er.examid
right join student s
on er.studentid = s.studentid
) x
pivot
(
max(flag)
for studentNameNum in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
The result will be:
| EXAMNAME | EXAMENDATE | CRAFT, PETER (14334) | DAVIS, ALAN (13432) | NEWMAN, TED (133123) | SMITH, JOHN (14323) |
----------------------------------------------------------------------------------------------------------------------------
| Exam 1 | 2013-01-01 12:00:00 | false | false | true | false |
| Exam 2 | 2013-01-01 14:00:00 | true | false | false | true |
| Exam 3 | 2013-01-02 12:00:00 | true | true | false | false |
| Exam 4 | 2013-01-02 14:00:00 | false | false | true | false |
| Exam 5 | 2013-01-03 12:00:00 | false | false | false | true |

concatenate rows and columns in sql server 2008

I have three columns FirstName,LastNAme,MiddleName
Table A
FirstNAme LastName MiddleName ID
James Tony peter 1
Jack Kallis end 1
nnnn nnn nnn 2
mmm mm aa 3
(there may be ten names with one id or one name with id )
Output for FullName where ID=1 should be:
FullName
James Tony Peter,Jack Kallis end
You can do this with the STUFF and FOR XML PATH method:
SELECT
STUFF(
(SELECT
',' + ISNULL(FirstName, '') + ' ' + ISNULL(LastName, '') + ' ' + ISNULL(MiddleName, '')
FROM
dbo.TableA
WHERE
ID = 1
FOR XML PATH('')), 1, 1, '')
This should rendered your desired output.

Resources