How to implement MAX function on a text column in SQL Server? - sql

I'm using SQL Server 2005 and have a column that contains serial numbers, which are nvarchar(50).
My problem is selecting max(serial_no) from the table. The serial numbers used to have a length of 7 only but new ones are now 15. Whenever I select the max, I get a result with a length of 7, which means that data is old. I also can't filter it to only select from records which have a length of 15 because then i'll miss some other data on my query.
Old serial numbers look like this...
'SNGD001'
..., and new ones look like this:
'SN14ABCD0000001'
Edit: I tried creating a dummy table without the old serial numbers (5 characters long), and I'm getting correct results.

As has been mentioned, your question is a bit hard to follow. If the max value could be either one of your old serial numbers or one of your new ones, I believe the following should do the trick:
SELECT MAX(RIGHT('0000000' + REVERSE(LEFT(REVERSE(YourTextColumn), PATINDEX('%[a-z]%', REVERSE(YourTextColumn)) - 1)), 7))
FROM YourTable
It finds the first non numeric character from the right keeping everything to the right of that. It then left zero pads the resulting numeric string to 7 characters and applies the MAX function.

Your question is a little tough to follow without good sample data to get a bearing on. I suggest for future, you show a few more examples of data to get better context, especially with sequencing. Now, your desire to get the MAX() of a "serial_no" from your table appears you need so you get detect the next sequential serial number to assign. However, your serial number appears to be a concatenation of a prefix string and then sequential. So, if I were to look at your brief data MIGHT HAVE BEEN along the lines of (last 3 digits are the sequential serializations)
SNGD001
SNGD002
SNGD003
...
SNGD389, etc...
and your new data with the last (last 7 digits are sequential serializations)
SN14ABCD0000001
SN14ABCD0000002
SN14ABCD0000003
...
SN14ABCD0002837
If this is correct, then you basically need to look at the max based on the leading 3 or 8 characters of the string PLUS the converted suffix numeric sequence. For starters, lets go with that to see if we are on the correct track or not, then you can easily concatenate the prefix and sequence number together at the end for determining the next available number.
So, based on the above samples, you may want to know that for each prefix, the last number of
SNGD389 and
SN14ABCD0002837 respective per their prefix
If the above is correct, I might start with...
select
case when LEN( RTRIM( yt.serial_no )) = 7
then LEFT( yt.serial_no, 4 )
else LEFT( yt.serial_no, 8 ) end as SerialPrefix,
MAX( case when LEN( RTRIM( yt.serial_no )) = 7
then CONVERT(INT, RIGHT( yt.serial_no, 3 ))
else CONVERT(INT, RIGHT( yt.serial_no, 7 )) end ) as SerialSequence
from
YourTable yt
group by
case when LEN( RTRIM( yt.serial_no )) = 7
then LEFT( yt.serial_no, 4 )
else LEFT( yt.serial_no, 8 ) end as SerialPrefix
Which would result in (based on sample data I presented)
SerialPrefix SerialSequence
SNGD 389
SN14ABCD 0002837
Of which since the serial sequence column being numeric, you could add 1 to it, then left-zero fill a string and concatenate the two back together such as to create
SNGD390
SN14ABCD0002838

Related

Count all elements in an array

I have a table that I save some data include list of numbers.
like this:
numbers
(null)
،42593
،42593،42594،36725،42592،36725،42592
،42593،42594،36725،42592
،31046،36725،42592
I would like to count the number elements in every row in SQL Server
count
0
1
6
4
3
You could use a replacement trick here:
SELECT numbers,
COALESCE(LEN(numbers) - LEN(REPLACE(numbers, ',', '')), 0) AS num_elements
FROM yourTable;
The above trick works by counting the number of commas (assuming your data really has commas as separators). For example, your last sample data point was:
,31046,36725,42592 => length is 18
310463672542592 => length is 15
Hence the difference in lengths correctly yields the right number of elements.
Another idea is to useSTRING_SPLIT:
SELECT y.numbers,
(SELECT COUNT(Value) - 1
FROM string_split(COALESCE(y.numbers,''),',')) AS num_elements
FROM yourtable AS y;
I know this looks a bit unhandy on first glance due to this strange -1 in the second line and the COALESCE in the third line. So why do I talk about this option?
Well, the strange thing in your case which causes these difficulties in my query is that your rows always start with a comma.
This is quite weird and it would be much easier without this first comma in every row.
Let's assume you remove this comma in future. Then this will become really easy and good readable:
SELECT y.numbers,
(SELECT COUNT(Value)
FROM string_split(y.numbers,',')) AS num_elements
FROM yourtable AS y;
Try out: db<>fiddle
your data
CREATE TABLE yourtable(
numbers VARCHAR(max)
);
INSERT INTO yourtable
(numbers) VALUES
(null),
('،42593'),
('،42593،42594،36725،42592،36725،42592'),
('،42593،42594،36725،42592'),
('،31046،36725،42592');
you need ISNULL and len
select
ISNULL(len(numbers) - len(replace(numbers,'،','')) ,0) count
from yourtable
the other way is by using IIF and string_split as follows
SELECT IIF(count < 0, 0, count) count
FROM   (SELECT (SELECT Count(*) - 1
                FROM   STRING_SPLIT (Replace(Replace(numbers, 'R', ''), '،',
                                     'R'), 'R'
                       )) AS
               'count'
        FROM   yourtable) A
dbfiddle

Getting different entries with different starting Char

enter image description here
Hey I'm looking to do a query which allows me to see how many citties have starting phone number with number 6 and the other between 7-9
SELECT COUNT(Ciudad) AS "T.Fijos", Ciudad
FROM "BBDD.CLIENTES"WHERE Teléfono LIKE "9%"
GROUP BY Ciudad
that query only shows me phone number starting with 9 I need to do all in one query
Please try the following.
However, I wonder whether your FROM should in fact be FROM "BBDD"."CLIENTES". By wrapping the entire name in double-quotes in that way, you are saying that the table name is "BBBD.CLIENTES" and not specifying a schema.
Unfortunately I am unable to test this on SQLite, but it should be generally OK.
We can use a common table expression (CTE - the WITH... part) to create a temporary view of the data including a new column for the starting digit.
Then we can use CASE WHEN ... to check what the start digit is and return some text describing that digit.
WITH t
AS
(
SELECT Ciudad,
"Teléfono",
CAST(SUBSTR(Teléfono,1,1) AS integer) AS starting_digit
FROM "BBDD.CLIENTES"
)
SELECT Ciudad,
'Teléfono starts with ' ||
CASE
WHEN starting_digit = 6 THEN '6'
WHEN starting_digit BETWEEN 7 AND 9 THEN '7, 8 or 9'
ELSE 'something else'
END AS phone_start,
COUNT(*) AS count_of_phone_numbers
FROM t
GROUP BY 1, 2;

Need to divide a date part in SQL Server

I have a column in my table with these values:
PING_TO_ME_20100828_Any87
TO_THESE_D_COLUMN_ENTRY_20200825
TO_THESE_D_20100829_COLUMN_ENTRY
201901_ARE_YOU_TRYING_TO_REACH47
ASK_TO_UOU_201008
I need to separate date values in a separate column.
My output should be:
20100828
20200825
20100829
201901
201008
Any help is very much appreciated.
You will (and already have) likely get comments about this telling you to fix your design. And while that is likely true...I won't try to pick apart why you are doing this, and I'll just give you the answer you came here for.
Your goal is to pick out either an 8 digit string of integers, or a 6 digit string of integers.
Here is one way you could do it:
SELECT x.y
, COALESCE(SUBSTRING(x.y, NULLIF(PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', x.y), 0), 8)
, SUBSTRING(x.y, NULLIF(PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9]%', x.y), 0), 6))
FROM (
VALUES ('PING_TO_ME_20100828_Any87'),
('TO_THESE_D_COLUMN_ENTRY_20200825'),
('TO_THESE_D_20100829_COLUMN_ENTRY'),
('201901_ARE_YOU_TRYING_TO_REACH47'),
('ASK_TO_UOU_201008')
) x(y)
Explanation:
Since you are looking for both 8 and 6 digit values, you need to check for the longer of the two first. So first I search for the occurrence of a string of 8 integers using:
NULLIF(PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', x.y), 0)
This returns the first position of a string of 8 integers. The reason I wrap it in a NULLIF() is because if the value is not found, then PATINDEX will return 0.
I use NULLIF() to return NULL in that case, essentially indicating nothing was found. If you pass a NULL value to SUBSTRING() then it also returns NULL.
This is all just a nice way of "failing over" to the 6 character string check.
So there I do the same thing again:
NULLIF(PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9]%', x.y), 0)
Except this time, I only repeat [0-9] six times. And again, I use the NULLIF() trick, so that it returns NULL if no string is found.
Throw that all into SUBSTRING() and COALESCE() and you've got a function that returns the results you're looking for.
Potential downsides
There are a couple down sides to this method.
It is not checking for a valid date, it's simply looking for a string of either 8 integers, or 6 integers. It could be 12345678 and it would still detect and return that.
If there are strings of integers longer than 8 digits, it will grab only the first 8 characters.
If there are multiple occurrences of 6 or 8 character integer strings...it will only return the first one.
There are much more robust ways you could write this, but it all depends on your data and what you need to do.
Other methods
Another way it could be done depending on which version of SQL Server you are using, is using STRING_SPLIT().
SELECT x.y, s.[value]
FROM (
VALUES ('PING_TO_ME_20100828_Any87'),('TO_THESE_D_COLUMN_ENTRY_20200825'),('TO_THESE_D_20100829_COLUMN_ENTRY'),('201901_ARE_YOU_TRYING_TO_REACH47'),('ASK_TO_UOU_201008')
) x(y)
CROSS APPLY (
SELECT [value]
FROM STRING_SPLIT(x.y, '_')
WHERE [value] LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
OR [value] LIKE '[0-9][0-9][0-9][0-9][0-9][0-9]'
) s
This method handles a couple of the downsides mentioned earlier. For example, it will ONLY return integer strings of length 6 or 8. It will also return ALL integer strings of length 6 or 8 and not just the first one.
And there's other ways to identify the strings as well, like using ISNUMERIC(x.[value]) or TRY_CONVERT(int, s.[value]).
It all depends on how you are using this code...if it's runs fast enough, and it's a one off script, then it really doesn't matter. If it's running for millions of records at a time, then yeah you should play around with other methods.

SQL Server - Combine string to integer where integer can have a variable number of leading zeros

I have a report in SQL Server Report Builder which brings back the profession acronym (string) and registration number (integer) for each professional in a separate SQL database.
The registration number can be 5 or more digits long, and may start with one or more zeros. For example:
Profession Registration #
AB 00162
PH 02272
SA 13925
SA 026025
DA 1025927
I'm trying to put the profession acronym and registration number together into a registration ID, because I need to compare this with the registration ID from another (non SQL) database.
I'm trying to get something like this:
Registration ID
AB00162
PH02272
SA13925
SA026025
DA1025927
I've tried converting the integers to strings using the following in my query:
REGISTRY.PROFESSION + right('00000' + cast(REGISTRY.REGISTRATION_NO as varchar(8)), 5) as Full_Reg_Number
However, with the above the integers that are more than 5 digits long get cut off, and if I increase '00000' to, say, '0000000' and the number '5' to '7' in the above, the integers that only have 5 digits are padded with extra leading zeros.
I do not have permission to change the formatting of the integers in either database.
Integers aren't stored with leading zeroes. To be stored like that, then the field is NOT of integer type in the first place. Simply do:
Registry.profession + registry.registration_no
You can confirm that the stored type is not an integer as follows:
select data_type
from information_schema.columns
where table_name = 'registry'
and column_name = 'registration_no'
If you're getting a type conversion error as you mention in your comments, then most likely the error is not coming due to this concatenation. It's probably down the line, such as if you're using 'Full_Reg_Number' in a 'where' statement or other comparison that expects a comparison to an integer, and instead is getting a varchar. After all, you called the column 'Full_Reg_Number' even though it's not a number.
Based on your problems, I suspect those really are integers. You've just shown them with leading zeros in the question.
A simple solution is to use case:
(REGISTRY.PROFESSION +
CASE WHEN REGISTRY.REGISTRATION_NO < 10000 THEN right('00000' + cast(REGISTRY.REGISTRATION_NO as varchar(8)), 5)
ELSE REGISTRY.REGISTRATION_NO
END
) as Full_Reg_Number
An even simpler method uses FORMAT():
(REGISTRY.PROFESSION + FORMAT(REGISTRY.REGISTRATION_NO, '00000')
) as Full_Reg_Number

Update column in postgresql

I found out that I have a character varaying column with mistakes in a database with over 4 millon records. It contains numbers. Each number has to have 12 digits, but for some reason a lot of those numbers ended up having 10 digits.
The good news is that the only thing I have to do, is prepend '55' to each cell that only has 10 digits and starts with the number '22', leaving the ones with 12 digits untouched.
My objective is this:
UPDATE
table
SET
column = CONCAT( '55', column )
WHERE
LENGTH( column ) = 10 AND column LIKE( '22%');
I am thinking of using this:
UPDATE
telephones
SET
telephone_number = CONCAT( '55', telephone_number )
WHERE
LENGTH( telephone_number ) = 10 AND telephone_number LIKE( '22%');
Am I doing it right? If not, what would be the correct way to do it
What if instead of a string the numbers were stored as big int, same rules apply, it is still 10 digits long which means the number is lower than 3.000.000.000 and bigger than 2.000.000.000? and they all need to be the same number starting with 55
The answer is: yes, that's right. You can play around with a sample database here on SQL Fiddle. That one uses the BIGINT type. Also see this one by #gmm, which uses the VARCHAR form. Both work just like you've described them using your original syntax.