Select area code from phone number entries - mysql

I want to select only the area code from a list of column entries populated by phone numbers. This is what I have:
SELECT LEFT(phone, 3) AS areacode, COUNT(phone) AS count
FROM registration
GROUP BY areacode;
The problem is, the entries aren't consistent. So some phone numbers start as +123-456-7899, and others with (123)-456-7899, and others with no symbol at the beginning.
So my question is: is there a way that I can ensure the SELECT LEFT starts at the first integer?
Thanks!

There are somethings that SQL is just not meant for. This is one. I would select the phone number into a string, and do some pattern matching in your programming language of choice to find the area code.
-OR-
Change your table such that area code is a different column.

Two options (neither of which being SQL):
Select all phone numbers and use a programming language of your choice to programatically strip out the unnecessary characters.
Clean the input to strip out all unnecessary characters prior to inserting them into the database
SQL is not the best way to do this, rather, SQL + programming

There actually is a way to do this in SQL that was intentionally designed for this exact purpose.
SELECT SUBSTRING(office_phone_number, 1, 3) FROM contact;
Of course, this depends on how the number is stored in the table. If parenthesis are present, your starting position would be off.
Here is more information:
MySQL substring function

Related

Sum columns with similar names in SQL

Relatively new to SQL and want to shorten a query I’m using.
The goal is to add the total spent in one year and compare it to the next year. However, the column names are all formatted “Spend_YYYYMM” so “Spend_202102.”
Currently, my solution is just to add all 12 columns up:
SELECT
“Full_Name”,
(“Spend_202001”+”Spend_202002”...) AS “2020 Total”,
(“Spend_201901”+”Spend_201902”...) AS “2019 Total”
FROM “Customers”
WHERE “2019 Total” > “2020 Total”;
So is there a way to look for columns where it starts with “Spend_2019” and add them up without having to type all 12 columns out? Or is what I have the only way we can really do this?
(Sorry for all the superfluous quotes, it’s apparently how our DB works with SQL.)
Thank you for your help!!
First, do not use identifiers that need to be escaped.
Second, your data model is weak. You should have separate rows for the different years.
But, the answer to your question is a MySQL extension of the HAVING clause:
SELECT Full_Name,
(Spend_202001 + Spend_202002 ...) AS Total_2020,
(Spend_201901 + Spend_201902 ...) AS Total_2019
FROM Customers
HAVING Total_2019 > Total_2020 ;

MySQL Invoice numbers range with count

Firstly I want this to be purely done with MySQL query.
I have a series of Invoice numbers
invoice_number
INV001
INV002
INV003
INV004
INV005
001
002
003
006
007
009
010
INVOICE333
INVOICE334
INVOICE335
INVOICE337
INVOICE338
INVOICE339
001INV
002INV
005INV
009INV
I want to output something like this
from_invoice_no to_invoice_no total_invoices
INV001 INV005 5
001 010 7
INVOICE333 INVOICE339 6
001INV 009INV 4
The invoice number pattern cannot be fixed. They can change in future
Please help me to achieve this.
Thanks in advance.
I will first show a general idea how to solve this problem and provide some code which will be ugly, but easily understandable. Then I'll explain what the issues are and how to remedy them.
STEP 1: Deriving the grouping criterion
For the first step, I assume you have the right (privilege) to create an additional column in your table. Let us name it invoice_text. Now, the general idea is to remove all digits from the invoice number so that only the "text pattern" remains. Then we can group by the text pattern.
Assuming that you have already created the column mentioned above, you could do the following:
UPDATE Invoices SET invoice_text = REPLACE(invoice_number, '0', '');
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '1', '');
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '2', '');
...
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '9', '');
After having done that, you will have the pure text pattern without digits in invoice_text and can use that for grouping:
SELECT COUNT(invoice_number) AS total_invoices FROM Invoices
GROUP BY invoice_text
This is nice, but it is not yet what you wanted. It does not show the first and last invoice number for each group.
STEP 2: Deriving the first and last invoice for each group
For this step, create one more column in your table. Let us name it invoice_digits. As the name implies, it is meant to take only the pure invoice number without the "pattern text".
Assuming you have that column, you could do the following:
UPDATE Invoices SET invoice_digits = REPLACE(invoice_number, 'A', '');
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'B', '');
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'C', '');
...
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'Z', '');
Now, you can use that column to get the minimum and maximum invoice number (without "pattern text"):
SELECT
MIN(invoice_digits) AS from_invoice_no,
MAX(invoice_digits) AS to_invoice_no,
COUNT(invoice_number) AS total_invoices
FROM Invoices
GROUP BY invoice_text
Problems and how to solve them
1) According to your question, you want to get the minimum and maximum full invoice number text. The solution above will show only the minimum and maximum invoice number text without the text parts, i.e. only the digits.
We could remedy this by doing a further JOIN, but since I can very well imagine that you won't insist on this :-), and since it won't make the general idea more clear, I am leaving this to you. If you are interested, let us know.
2) It might be difficult to decide what a digit (i.e. what the actual invoice number) is. For example, if you have invoice numbers like INV001, INV002, this will be no problem, but what if you have INV001/001, INV001/002, INV002/003 and so on? In this example, my code would would yield 001001, 001002, 002003 as actual invoice numbers and use that to decide what the minimum and maximum numbers are.
This might not be what you want to do in that case. The only way around this is that you thoroughly think about what you should consider a digit and what not, and to adapt my code accordingly.
3) My code currently uses string comparisons to get the minimum and maximum invoice numbers. This may yield other results than comparing the values as numbers. If you are wondering what that means: Compare '19' to '9' as string, and compare 19 to 9 as number.
If this is a problem, then use MySQL's CAST to convert the text to a number before feeding it to MAX or MIN. But please be aware that this has its own caveats:
If you have very long invoice numbers with so many digits that they don't fit into MySQL's numeric data types, this method will fail. It will also fail if you have defined a character like / to be digits (due to the issues described in 2)) since MySQL can't convert this into a number.
Instead of converting to numbers, you can also pad the values in invoice_digits with leading zeroes, for example using MySQL's LPAD function. This will avoid the problems described above and sort the numbers as expected, even if they include non-digits like /, but you will have to know the maximum length of the digit string in advance.
4) The code is ugly! Do you really have to remove all possible characters from A to Z one by one by doing UPDATE statements to get the digit string?
Actually, it is even worse. I just have assumed that you only have the "text characters" A to Z in your invoices. But there could be any character Unicode defines: Russian or Chinese ones, special characters, in other words: thousands of different characters.
Unfortunately, AFAIK, MySQL still does not provide a REGEX-REPLACE function. I don't see any chance to get this problem solved unless you extend MySQL with an appropriate UDF (user defined function). There are some cool guys out there who have recognized the problem and have added such functions to MySQL. Since recommending libraries seems to be discouraged on SO, just google for "mysql regex replace".
When having extended MySQL that way, you can replace the ugly bunch of UPDATE statements which remove the digits / the text from the invoice number by a single one (using a REGEX, you can replace all digits or all non-digits at once).
For the sake of completeness, you could avoid the many UPDATE statements by doing UPDATE ... SET ... = REPLACE(REPLACE(REPLACE(...))) and thus applying all updates with one statement. But this is even more ugly and error prone, so if you are serious about your problem, you'll really have to extend MySQL by a REGEX-REPLACE.
5) The solution will only work if you have the privilege to create new columns in the table.
This is true for the solution as-is. But I have chosen to go that way solely because it makes the general idea clear and understandable. Instead of adding columns to your original table, you could also create a new table where you store the pure text / digits (this table might be a temporary one).
Furthermore, since MySQL supports grouping by computed values, you don't need additional columns / tables at all. You should decide by yourself what is the best way to go.

How to order text that contains double colons (::)

To order by name I'm using 'order by name'
But the names contain double colons : '::'
How can I order by the text that occurs subsequent to the double colons ?
So :
aaaa::bbbb
aaaa::aaaa
aaaa::1234
aaaa::a1234
Will be ordered :
aaaa::1234
aaaa::aaaa
aaaa::a1234
aaaa::bbbb
Order by the substring ans use locate to find where it starts:
order by substring(name, locate('::', name) + 3, 30)
It'll decrease performance since no index will be used.
You would have to create a new field in MySQL then insert the second part of your text into it. Sort by uses various indexes and algorithms (such as divide and conquer).
As such it would not work on sorting on a specific portion of a specific string, and if you did manage to 'fake' a way of doing it, the performance would be terrible due to lack of indexes.
Sorry, I realise this probably isn't the answer your looking for, but I'm afraid the best way is the slightly longer way, but at least you can then do it at lighting fast speeds if you add an index to it :)
You must split the text into two columns and order by the latter one. You can either split and join the columns in application code or use views and stored procedures to make it look like one column to a database client.
about your sorting , according to ascii values numbers come first before alphabets,
so aaaa:1234 should come first
You can retrieve the values and sort in PHP
Navsort
<?php
$arr = array("aaaa::bbbb","aaaa::aaaa","aaaa::1234","aaaa::a1234");
$sec=$arr;
natsort($sec);
print_r ($sec);
?>
You may try the following approach
Get all records where All data is Alphabet after ::
UNION
Get all records where All data is Numeric after ::

MySQL UNION query correct handling for 3 or more words

I've to ask your help to solve this problem.
My website has a search field, let's say user writes in "Korg X 50"
In my database in table "products" i have a filed "name" that holds "X50" and a field "brand" that hold "Korg". Is there a way to use the UNION option to get the correct record ?
And if the user enters "Korg X-50" ?
Thank you very much !
Matteo
May be you should use full-text search
SELECT brand, name, MATCH (brand,name) AGAINST ('Korg X 50') AS score
FROM products WHERE MATCH (brand,name) AGAINST ('Korg X 50')
As far as I understand you don't need UNION but something like
SELECT * FROM table1
WHERE CONCAT(field1, field2) LIKE '%your_string%'
On client side you get rid of all characters (like space, hyphen, etc) in your_string that appears in user input and cannot be in field1 or field2.
So, user input Korg X 50 as well as Korg X-50 becomes KorgX50.
you will need to get some form of searchable text.
either parse out the input for multiple key words and match each separately, or perhaps try to append them all together and match to the columns appended in the same way.
you will also need either a regex, or maybe a simpler search and replace to get rid of spaces and dashes after the append before the comparison.
in general, allowing users to search for open ended text strings is more complicated than 'what union do i use'... you will ideally also be worried about slight misspellings and capitalization, and keyword order.
you may consider pulling all keywords out from your normal record into a separate keyword list associated with each product, then use that list to perform your searches.
If you do not want to parse user input and use as it is, then you will need to use a query like this
select * from products where concat_ws(' ',brand,name) = user_input -- or
select * from products where concat_ws(' ',brand,name) like %user_input%
However, this query won't return result if user enters name "Korg X-50" and your table contains "Korg" and "X50", then you need to do some other thing to achive this. You may look at http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_soundex however it won't be a complete solution. Look for text indexing libraries for that ex: lucene

extracting strings from mysql field

total slow moment day, i need to extract different areas based on what language is selected from a field in a mysql database
ex:
<!--:en-->Overview<!--:--><!--:es-->Overview<!--:--><!--:fr-->Présentation<!--:--><!--:ar-->نظرة عامة<!--:-->
so if my language is french for example, i want the part between <!--:fr--> and <!--:-->
any ideas?
Strings processing is not the strongest part of MySQL. But here is one idea:
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '<!--:fr-->', -1), '<!--:-->', 1) FROM table_name
The easier way would be using a substring. You can find the index for the language on the string first. After that, find the index of the end marker () and extract what's in the middle, which is the value you want.
A more elaborated way would be using regular expressions. The implementation depends on the language you are coding on.