Splitting a value in SQL cell - sql-server

Hello all so I have a question regarding SQL.
In my database I have a table with 4 rows (key , value , help , description) in value i have a long name something like : Howard Beach 15 678 Mill Grave ave p.o box.
Is it possible to change this field so it can look like this :
Howard Beach 15
678 Mill Grave ave
p.o box
I'am not talking about using SELECT I would like to insert some line breaker or something into the database so this addres is displayed in html not in one line but compacted. Because now it reads it from the database as one big line.
Does SQL have any separators that i could write to the database to change this viewing ?
I have SQL Server 2016.
I tried manually change something in the value cell but SQL has no features like that. Would be great if "enter" worked but no luck.

Whichever solution is best is depended of your specific application and data intake process, how much work it would take to implement the changes, what changes are going to save you the most work over time, what follows best practices, and keeps your data in the most meaningful, useful format.
A. The easiest solution, although this may not be best, is probably to use CSS styling to alter the appearance of the address text on the front-end. Normally if that that is the job of the front-end person/people. Placing the value inside a sized div would keep a really long address line from appearing on a page.
.sizedDiv {
width: 150px;
height: 400px;
background-color: #e2e2e2;
}
<html>
<body>
<div class="sizedDiv">
<p class="valueP"> Address Recipient, Address Line 1, Address Line 2, etc.</p>
</div>
<body>
</html>
B. Another possible solution would be changing the way values are stored in the db, by dividing values up into different parts (Value_Line_1, Value_Line_2, etc.) in the table, and then pulling out those fields and joining them as needed on the front-end. Of course depending on how the application is constructed, this may take some work depending on how values are imported, especially is data is imported from an external source and you have no control over the intake process for new addresses.
C. Adding a char(10), or any other character, and then splitting on the front-end, which is the direction the conversation above seemed to be leading, is a solution that would introduce a possibility for errors, in the case where that character appears unexpectedly elsewhere in the address text.
D. It is also possible to divide up value strings arbitrarily based on a specific character length, here's an example in SQL Server format:
DECLARE #addressLength INT = 25;
SELECT SUBSTRING([value], 1, (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))) ) AS line_1,
SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))) + 1,
(#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))), #addressLength))))
) AS line_2,
SUBSTRING([value], #addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength))) + (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))), #addressLength)))) + 1, 30 ) AS line_3
From Value_Table_Here;
this could be used to divide up values into parts in your table, although this solution would not divide up data in a meaningful pieces (i.e. Address Line 1, Address Line 2, etc.) You could save these address/value parts in a view that can be delivered to the application.
E. An alternate method of this would be using a similar query to above to just inserting <br/> elements at certain points in the address/value string:
DECLARE #addressLength INT = 25;
SELECT SUBSTRING([value], 1, (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))) ) +'<br/>'+
SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))) + 1,
(#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))), #addressLength))))
) +'<br/>'+
SUBSTRING([value], #addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength))) + (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], (#addressLength - CHARINDEX(' ',REVERSE(SUBSTRING([value], 1, #addressLength)))), #addressLength)))) + 1, 30 ) AS formattedAddress
From Value_Table_Here;
the formatted address or values could be saved in a view and delivered to the application as well.

Related

Formatting Phone Numbers in Non-US format

I have the following code that I'm trying to refactor to format a phone number field in the format I need it in:
STUFF(STUFF(STUFF(REPLACE('02 212345678','02 2','02 '), 7, 0, ' '), 3, 0, ') '), 1, 0, '(')
It returns data currently as this:
(02) 123 45678
where I need it in this format
(02) 1234 5678
The problem is the extra space after the closing bracket and having 4 numbers either side.
Based on your example, does the following work for you?
with sampledata as (select '02 212345678' num)
select Concat(Stuff('() ',2,0,Left(num,2)), Stuff(Right(num,8),5,0,' '))
from sampledata
You existing code works, just need to change the 7 to an 8, but I far prefer to keep my formatting and data seperated. The code is waaaaaay easier to read and modify:
DECLARE #PhoneNumber varchar(20) = '02 212345678';
SELECT FORMATMESSAGE('(%s) %s %s', LEFT(#PhoneNumber, 2), SUBSTRING(#PhoneNumber, 4, 4), SUBSTRING(#PhoneNumber, 8, 4) );

Is there any way to combine the functionality of LIKE and BETWEEN in a query?

Im working on a query where the most obvious solution would be to combine the functionality of Like and Between, but im not sure if its possible.
My goal is to get ID's between a certain range, where the IDs are constructed of a leading code, followed by datetime, and the possible a couple more caracters like so:
ABC180715051623XYZ
The range would be from the current time, to 10 minutes prior. the leading characters don't matter for what is chosen, just the date time numbers in the center. an additional issue is that these leading characters can vary in length, somtimes 2, and other times 4.
On thatnote, ive been trying to use wildcards, and the like function, but they dont work as needed on there own. Is there any way to combine them?
Thank You
Assuming that:
The leading ID characters are always letters (actually just not numbers)
The date is always in the format yyMMddhhmmss
Then you could have your query as such:
SELECT * FROM [your_table]
WHERE CONVERT( -- Converts to a datetime
DATETIME, STUFF( -- STUFF #1
STUFF( -- STUFF #2
STUFF( -- STUFF #3
SUBSTRING([id_column],
PATINDEX('%[0-9]%',[id_column]), -- gets the index of the first number
12), -- gets the 12 digit date string (SUBSTRING)
7, 0, ' '), -- adds a space between date and time portions (STUFF #3)
10, 0, ':'), -- adds a ':' between hours and minutes (STUFF #2)
13, 0, ':')) -- adds a ':' between minutes and seconds (STUFF #1)
BETWEEN DATEADD(minute,-10, CURRENT_TIMESTAMP) -- 10 minutes ago
AND CURRENT_TIMESTAMP; -- now
This is taking the date and time part of the ID and forming it into a string that can then be converted into a datetime which can then be used in a the between of 10 mins ago and now. I've tried to format it so that it can be read but I'll explain the parts from the inside out below so you can edit if required.
Using your given value for ID of
ABC180715051623XYZ
First PATINDEX('%[0-9]%',[id_column]) This gets the (1-based) index first number in the id column. So this would be 4
This makes SUBSTRING([id_column], 4, 12) which gets out 180715051623
So then STUFF('180715051623', 7, 0, ' ') puts a space at the 7th index, giving 180715 051623
Then STUFF('180715 051623', 10, 0, ':') puts a ':' at the 10th index, giving 180715 05:1623
Then STUFF('180715 05:1623', 13, 0, ':') puts a ':' at the 13th index, giving 180715 05:16:23
This is then converted into the date '2018-07-15 05:16:23.000' and then used in the the between clause of two other datetimes.

How to convert GPS exif to geography?

I have attachments table which has GPSLatitude and GPSLongitude columns for each attachment. It's legacy code which is populating the fields and the values looks like:
GPSLatitude
50/1,5/1,1897/100
GPSLongitude
14/1,25/1,4221/100
Is there any build in function which I can use in order to convert them to latitude and longitude decimal values like this:
Location Latitude
41.5803
Location Longitude
-83.9124
I can implement SQL CLR function if this can be done easier with .net also.
What is most difficult for me right now is to understand what these values represent. The legacy code is using some API with no documentation about the returned format and how to read it.
The values above are just for showing how the data is formatted. The following library is used to get the values - chestysoft like this:
IF Image.ExifValueByName("GPSLatitude") <> "" THEN GPSLatitude = Image.ExifValueByName("GPSLatitude") ELSE GPSLatitude = NULL END IF
IF Image.ExifValueByName("GPSLongitude") <> "" THEN GPSLongitude = Image.ExifValueByName("GPSLongitude") ELSE GPSLongitude = NULL END IF
I'm fairly certain you should read it as:
50/1: 50 Degrees
5/1: 5 Minutes
1897/100: 18.97 Seconds
This would put the location you've given in New York (assuming N/W), does that make sense? If you have no way to validate the output it's very difficult to make any other suggestion... See also here
In the link you provided, you can upload a picture to view the exif data. There you can test with known locations. It is also apparent that in the values you mentioned, the GPSLatitudeRef and GPSLongitudeRef are missing. You need these to change the values to a location. Do you have those values in your table? Otherwise you'll have to make (wild) assumptions.
This is by the way the standard EXIF notation for latitude/longitude; I assume there are many, many tools to convert it.
Assuming that #Cool_Br33ze is correct, and the data is in degrees, minutes and seconds, you can calculate the values you need using the following:
declare #v varchar(30) = '50/1,5/1,1897/100'
select #v Original_Value,
convert(decimal(18,4), left(#v, charindex('/', #v) - 1)) [Degrees],
convert(decimal(18,4), substring(
#v,
charindex(',', #v) + 1,
charindex('/', #v, charindex(',', #v)) - (charindex(',', #v) + 1)
) / 60.0
) [Minutes],
convert(decimal(18,4), substring(
#v,
charindex(',', #v, (charindex(',', #v) + 1)) + 1,
charindex('/', #v, charindex(',', #v, (charindex(',', #v) + 1))) - (charindex(',', #v, (charindex(',', #v) + 1)) + 1)
) / 360000.0
) [Seconds]
It looks a bit of a mess, but it splits out the degrees, minutes and seconds (converted to DECIMAL(18,4)), all you need to do is add the three values together to get your Lat/Long value in degrees.
I'd test it thoroughly before implementing it though.

SQL Server concatenation not working with plus

I am attempting to concatenate two strings using "+", both these strings derive from a substring which have each been cast to varchar(20) from ntext fields that do not allow nulls.
When I run the code, it only takes the first of the two strings, and ignores the second, so the concatenation is unsuccessful. Any idea what I am doing wrong or missing here?
Select Top 100
Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20) As PossFirst,
Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1)) As PossLast,
Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20) +
Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1)) As PossLogin
from V_WOI_WorkOrder w
What I'm getting is the Following:
PossFirst PossLast PossLogin
----------- ----------- -----------
james roberts james
mark smith mark
harry chapman harry
What i should be getting is:
PossFirst PossLast PossLogin
----------- ----------- -----------
james roberts jamesroberts
mark smith marksmith
harry chapman harrychapman
The reason for my len within my substring is that the complete concatenation should not exceed 20 characters long, I'm not finished that part yet, but i think that is irrelevant to the point that the concatenation is failing for me.
Screenshot of results
Hmm, I'm not sure about the issue yet, but we can try doing this ..
SELECT PossFirst, PossLast, ISNULL(PossFirst,'') + ISNULL(PossLast,'') AS PossLogin FROM (
Select
Convert(nvarchar(20),Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) As PossFirst,
Convert(nvarchar(20),Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1))) As PossLast,
from V_WOI_WorkOrder w
) AS T
EDIT : This is wierd, I have this table
and tried this
select PossFirst, PossLast, PossFirst + PossLast from (
select
Substring(Cast(w.firstname As nvarchar(20)), 1, 20) As PossFirst
, Substring(Cast(w.lastname As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.lastname As nvarchar(20)), 1, 20)) + 1)) As PossLast
from nTextDataTypeTest w
) AS T
with this result...
I wonder with what column data types you have, and I wonder what SQL version you're using..
EDIT : Lets try trimming them..
SELECT PossFirst, PossLast, ISNULL(PossFirst,'') + ISNULL(PossLast,'') AS PossLogin FROM (
Select
Rtrim(Ltrim(Convert(nvarchar(20),Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)))) As PossFirst,
Rtrim(Ltrim(Convert(nvarchar(20),Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1))))) As PossLast,
from V_WOI_WorkOrder w
) AS T
Weather using nText, or a varchar field that has originally derived from an nText field, a visual count on the characters differed from a Len on the characters by a total of 1 character.
A visual count on the characters showed for example 12 characters. A len on the [field] showed 13 characters, so when I substring [field] to len[field]-1 as shown below, I am then able to concatenate to it.
SUBSTRING([Field], 1, (Len([Field]) - 1)
What we have done is trimmed off the trailing hidden/special character that is stopping our concatenation from working.
CAST(SUBSTRING([Field], 1, (Len([Field]) - 1) As varchar(20))
We then Cast our result to varchar, in order to match the other other fields that we wish to concatenate.
I now have my final code using my fields and tables below which works successfully!
Select Cast(SUBSTRING(w.WO_Type_Field_02,1,(Len(w.WO_Type_Field_02)-1)) As varchar(20)) As PossFirst,
Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1)) As PossLast,
Cast(SUBSTRING(w.WO_Type_Field_02,1,(Len(w.WO_Type_Field_02)-1)) As varchar(20)) + '.' +
Substring(Cast(w.WO_Type_Field_04 As nvarchar(20)), 1, (20 -
Len(Substring(Cast(w.WO_Type_Field_02 As nvarchar(20)), 1, 20)) + 1)) As PossLogin
from V_WOI_WorkOrder w
The problem here is your ntext fields contain whitespace. These hidden characters are eating into your 20 character limit.
There are two easy ways to spot whitespace. You an use the LEN function and/or concatenate a leading and trailing character.
I've created an example on stack data exchange (link and code below) using a table variable. The benefit of this approach is we can all share the same code.
In this example james is postfixed with a number of blank spaces. The other records are not. As you can see james is returned without a surname. The others are not. This happens because the spaces count towards the 20 character limit. Using RTRIM will remove these.
RTRIM in SQL Server only works on spaces. If your whitespace is made from other control characters you will need a different approach.
I've replaced the SUBSTRINGs with a LEFT, which will achieve the same result in fewer steps.
EDIT 1: To include description of original problem and solution.
EDIT 2: Answer rewritten after the OPs issue was solved. New answer better explains the issue and solution.
Example
/* Let's make a variable that we can all share.
*/
DECLARE #SampleData TABLE
(
FirstName NTEXT NOT NULL,
LastName NTEXT NOT NULL
)
;
/* Populate the table with sample values for us
* to experiment with.
* Postfix james with blank spaces.
*/
INSERT INTO #SampleData
(
FirstName,
LastName
)
VALUES
('james ', 'roberts'),
('mark', 'smith'),
('harry', 'chapman')
;
/* CAST and CONCATENATE the NTEXT fields.
* SUBSTRING replaced with LEFT for simplicity.
* Pipe added to end of each string to show its length.
*/
SELECT
FirstName,
LastName,
LEFT(CAST(FirstName AS NVARCHAR(20)) + CAST(LastName AS NVARCHAR(20)), 20) + '|' AS FirstLastName_NoTRIM,
LEFT(RTRIM(CAST(FirstName AS NVARCHAR(20))) + RTRIM(CAST(LastName AS NVARCHAR(20))), 20) + '|' AS FirstLastName_WithTRIM
FROM
#SampleData
;

How to extract this specific substring in SQL Server?

I have a string with a specific pattern:
23;chair,red [$3]
i.e., a number followed by a semicolon, then a name followed by a left square bracket.
Assuming the semicolon ; always exists and the left square bracket [ always exists in the string, how do I extract the text between (and not including) the ; and the [ in a SQL Server query? Thanks.
Combine the SUBSTRING(), LEFT(), and CHARINDEX() functions.
SELECT LEFT(SUBSTRING(YOUR_FIELD,
CHARINDEX(';', YOUR_FIELD) + 1, 100),
CHARINDEX('[', YOUR_FIELD) - 1)
FROM YOUR_TABLE;
This assumes your field length will never exceed 100, but you can make it smarter to account for that if necessary by employing the LEN() function. I didn't bother since there's enough going on in there already, and I don't have an instance to test against, so I'm just eyeballing my parentheses, etc.
Assuming they always exist and are not part of your data, this will work:
declare #string varchar(8000) = '23;chair,red [$3]'
select substring(#string, charindex(';', #string) + 1, charindex(' [', #string) - charindex(';', #string) - 1)
An alternative to the answer provided by #Marc
SELECT SUBSTRING(LEFT(YOUR_FIELD, CHARINDEX('[', YOUR_FIELD) - 1), CHARINDEX(';', YOUR_FIELD) + 1, 100)
FROM YOUR_TABLE
WHERE CHARINDEX('[', YOUR_FIELD) > 0 AND
CHARINDEX(';', YOUR_FIELD) > 0;
This makes sure the delimiters exist, and solves an issue with the currently accepted answer where doing the LEFT last is working with the position of the last delimiter in the original string, rather than the revised substring.
select substring(your_field, CHARINDEX(';',your_field)+1
,CHARINDEX('[',your_field)-CHARINDEX(';',your_field)-1)
from your_table
Can't get the others to work. I believe you just want what is in between ';' and '[' in all cases regardless of how long the string in between is. After specifying the field in the substring function, the second argument is the starting location of what you will extract. That is, where the ';' is + 1 (fourth position - the c), because you don't want to include ';'. The next argument takes the location of the '[' (position 14) and subtracts the location of the spot after the ';' (fourth position - this is why I now subtract 1 in the query). This basically says substring(field,location I want substring to begin, how long I want substring to be). I've used this same function in other cases. If some of the fields don't have ';' and '[', you'll want to filter those out in the "where" clause, but that's a little different than the question. If your ';' was say... ';;;', you would use 3 instead of 1 in the example. Hope this helps!
If you need to split something into 3 pieces, such as an email address and you don't know the length of the middle part, try this (I just ran this on sqlserver 2012 so I know it works):
SELECT top 2000
emailaddr_ as email,
SUBSTRING(emailaddr_, 1,CHARINDEX('#',emailaddr_) -1) as username,
SUBSTRING(emailaddr_, CHARINDEX('#',emailaddr_)+1, (LEN(emailaddr_) - charindex('#',emailaddr_) - charindex('.',reverse(emailaddr_)) )) domain
FROM
emailTable
WHERE
charindex('#',emailaddr_)>0
AND
charindex('.',emailaddr_)>0;
GO
Hope this helps.

Resources