MySQL - sort by certain last string character - mysql

I'm trying to sort by a certain character on a string, for example,
before:
+----+---------+
| id | name |
+----+---------+
| 1 | red |
| 2 | red-a |
| 3 | red-xy |
| 4 | blue |
| 5 | blue-a |
| 6 | blue-xy |
+----+---------+
after:
+----+---------+
| id | name |
+----+---------+
| 4 | blue |
| 1 | red |
| 5 | blue-a |
| 2 | red-a |
| 6 | blue-xy |
| 3 | red-xy |
+----+---------+
are there any ways to categorize based on -a or -xy using ORDER BY
Thank you in advance.

SELECT
CASE
WHEN RIGHT(`name`,LENGTH(`name`)-INSTR(`name`,'-')) = `name` THEN ''
ELSE RIGHT(`name`,LENGTH(`name`)-INSTR(`name`,'-'))
END AS `suffix`,
`name`
FROM
`table1`
ORDER BY
`suffix`, `name`
If no suffix is found, it will put the record in the first row set.
Caveat: the first dash is used to separate the word from the prefix.

This will do what you're looking for. Wouldn't like to promise great performance if you had a lot of rows though:
select id, name from
(
select id,
name,
if (substring_index(name,'-', -1) = name, '', substring_index(name,'-', -1)) as grouping
from Table1
order by grouping, name
) as subTable
SQLFiddle here
[EDIT] Actually, that can be simplified to a single select with :
select id,
name
from Table1
order by if (substring_index(name,'-', -1) = name, '', substring_index(name,'-', -1)), name

These queries are more readable and this is probably the easiest way to sort on a suffix
SELECT
*
, IF (LOCATE('-', name) = 0
, 0
, LENGTH(SUBSTRING_INDEX(name, '-', -1))
)
suffix_length
FROM
Table1
ORDER BY
suffix_length
;
SELECT
*
FROM
Table1
ORDER BY
IF (LOCATE('-', name) = 0
, 0
, LENGTH(SUBSTRING_INDEX(name, '-', -1))
)
;
See demo http://sqlfiddle.com/#!9/92b63/41

Related

Converting CHAR Primary Key to INT in MySQL/MariaDB

I have a table that uses CHAR as the primary key for customers. I am attempting to load this table into a schema such that the primary key should be an INT.
DROP TABLE IF EXISTS `customers`;
CREATE TABLE `customers` (
`customer_id` char(5) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `customers` VALUES ('99944'),('99946'),('99976'),('A0014'),('A0049'),('A0124'),('C01AH'),('C01AQ'),('C01AW'),('C01AX'),('C01AY'),('C01AZ');
Fiddle
I have attempted variations on select cast(customer_id AS UNSIGNED) FROM customers; but only get back 0s for the non-int rows. How do I cast the non-int rows into a consistent INT result?
The ideal result would look like this:
For customer IDs that are solely integers, leave them alone.
For customer IDs that contain any letter, replace everything in the ID with a unique numerical identifier.
Expected result:
SELECT * FROM Customers;
`customer_id`
-------
99944
99946
99976
13871911
13871912
13871913
13872128
13872229
13872293
13872505
13872512
13872561
GMB did give me a other idea.
Using the HEX() and CONV(.., 16, 10) to convert from hexadecimals into decimales
Query
SELECT
customers.customer_id
, CASE
WHEN (customers.customer_id >> 0) > 0
THEN customers.customer_id >> 0
ELSE
CONV(HEX(customers.customer_id), 16, 10)
END
AS customer_id_int
FROM
customers;
Result
| customer_id | customer_id_int |
| ----------- | --------------- |
| 99944 | 99944 |
| 99946 | 99946 |
| 99976 | 99976 |
| A0014 | 279981338932 |
| A0049 | 279981339705 |
| A0124 | 279981404724 |
| C01AH | 288571343176 |
| C01AQ | 288571343185 |
| C01AW | 288571343191 |
| C01AX | 288571343192 |
| C01AY | 288571343193 |
| C01AZ | 288571343194 |
p.s
It might be generating a to large int you need to use a BIGINT datatype.
see demo
Updated
A other method to generate smaller int's (UNSIGNED INT) which uses a "SQL number generator", SUBSTRING(), ORD() and GROUP_CONCAT().
Query
SELECT
customers.customer_id
CASE
WHEN customers.customer_id >> 1 > 0
THEN customers.customer_id
ELSE
GROUP_CONCAT(
CASE
WHEN SUBSTRING(customers.customer_id, number_generator.number, 1) NOT BETWEEN 'A' AND 'Z'
THEN SUBSTRING(customers.customer_id, number_generator.number, 1) >> 1
ELSE ORD(SUBSTRING(customers.customer_id, number_generator.number, 1))
END
ORDER BY
number_generator.number ASC
SEPARATOR ''
)
END
) AS customer_id_int
FROM (
SELECT
record_1.number
FROM (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) AS record_1
) AS number_generator
CROSS JOIN
customers
GROUP BY
customers.customer_id
ORDER BY
customers.customer_id ASC
Result
| customer_id | customer_id_int |
| ----------- | --------------- |
| 99944 | 99944 |
| 99946 | 99946 |
| 99976 | 99976 |
| A0014 | 650002 |
| A0049 | 650024 |
| A0124 | 650012 |
| C01AH | 67006572 |
| C01AQ | 67006581 |
| C01AW | 67006587 |
| C01AX | 67006588 |
| C01AY | 67006589 |
| C01AZ | 67006590 |
see demo
With Maria DB >= 10.0.5, here is a solution to turn a string primary key to an integer primary key in a predictable manner :
SELECT
customer_id old_id,
CAST(
REGEXP_REPLACE(customer_id, '([^0-9])', ORD('$1'))
AS UNSIGNED
) new_id
FROM customers;
REGEXP_REPLACE() captures non-numeric characters (anywhere in the string) and ORD() turns each of them into its ordinal (numerical) represtation.
Demo on DB Fiddle :
old_id | new_id
:----- | -------:
99944 | 99944
9Z946 | 936946
A9CZ6 | 36936366
A0C14 | 3603614
0ABC0 | 3636360
Using MySQL 8.0 REGEXP_REPLACE:
select cast(REGEXP_REPLACE(customer_id,'[^0-9]','') AS UNSIGNED) FROM customers;
db<>fiddle demo

get empty instead of repeated value in query

I have a table like this
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 2 | 1|efg | 1 |
| 3 | 1|cde | 1 |
| 4 | 2|zzz | 1 |
I want to run a query like this:
SELECT * FROM table WHERE prj=1 ORDER BY name
but printing out repeated values only once. I want to keep all the rows and I would like to do this at database level and not on the presentation layer (I know how to do it in php).
Desired result is
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 3 | |cde | 1 |
| 2 | |efg | 1 |
| 4 | 2|zzz | 1 |
any hint on where to start from to build that query?
Use a session variable to test if the previous ID is the same as the current ID:
SELECT num, IF(#lastid = id, '', #lastid := id) AS id, name, prj
FROM table
CROSS JOIN (SELECT #lastid := null) x
ORDER BY table.id, name
DEMO
Note that you need to qualify table.id, because ORDER BY defaults to using the alias from the SELECT list if it's the same as a table column, and that would order the empty fields first.

Mysql - Check if VARCHAR column has a missing value on its incrementation

I'm trying to find out if my values inserted are auto-incrementing correctly or if for any reason one has failed to be inserted, deleted or gone "missing". I've tried several answers from Stackoverflow but they were mainly pointing out autoincrementable int values so they did not help since mine is a VARCHAR value that follows the following sequence:
AA000001
AA000002
...
AA000100
...
AA213978
and so on...
Thanks for your time.
You can declare SQL Vars in Query and calculate the difference in each iteration, as shown in the example below:
Schema
create table MyTable
( ai int auto_increment primary key,
id varchar(100) not null
);
insert MyTable (id) values
('AA000001'),
('AA000002'),
('AA000005'),
('AA000008'),
('AA000009'),
('AA000010');
Query
select id
FROM
(
select
t.id,
SUBSTRING(t.id,3) as s,
CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId as diff,
if( #lastId = 0, 0, CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId) as Difference,
#lastId := CAST(SUBSTRING(t.id,3) AS UNSIGNED) as dummy
from
`MyTable` t,
( select #lastId := 0) SQLVars
order by
t.id
) d
WHERE diff>1;
This is the inside query (not the final result set of the above)
+----------+--------+------+------------+-------+
| id | s | diff | Difference | dummy |
+----------+--------+------+------------+-------+
| AA000001 | 000001 | 1 | 0 | 1 |
| AA000002 | 000002 | 1 | 1 | 2 |
| AA000005 | 000005 | 3 | 3 | 5 |
| AA000008 | 000008 | 3 | 3 | 8 |
| AA000009 | 000009 | 1 | 1 | 9 |
| AA000010 | 000010 | 1 | 1 | 10 |
+----------+--------+------+------------+-------+
Actual Results of Above Query:
+----------+
| id |
+----------+
| AA000005 |
| AA000008 |
+----------+
Here's the SQL Fiddle.
To simply test if there are missing values,
select count(*) <> max(right(col, 6))-min(right(col, 6))+1 || count(*) <> count(distinct col)

MySQL syntax to combine row count in one table with select results from another table

In my MySQL database I have two tables.
Table contact_groups:
+-------------+-----------+
| groupname | fieldname |
+-------------+-----------+
| Wholesalers | grp_whs |
| Retailers | grp_rtl |
| Consumers | grp_cns |
+-------------+-----------+
Table contacts:
+---------+---------+---------+---------+
| name | grp_whs | grp_rtl | grp_cns |
+---------+---------+---------+---------+
| Tom | 0 | 1 | 1 |
| Dick | 1 | 0 | 0 |
| Harry | 0 | 1 | 0 |
| John | 0 | 1 | 1 |
| Jane | 1 | 1 | 0 |
| Anna | 1 | 0 | 0 |
| Bob | 0 | 0 | 1 |
| Charlie | 0 | 1 | 1 |
+---------+---------+---------+---------+
I need to write a single qyery that returns a list of field names and group names from the contact_groups table, with the number of names from the contacts table associated with that group concatenated to the group name. In case of the above data, that would return the following:
+-----------+-----------------+
| fieldname | groupname |
+-----------+-----------------+
| grp_whs | Wholesalers (3) |
| grp_rtl | Retailers (5) |
| grp_cns | Consumers (4) |
+-----------+-----------------+
(The reason why I need a single query producing the above output in this format is that this statement will be executed by a form generator that lack flexibility and can only execute one single query that must return fieldname and group names in this manner.)
How do I do this?
If you want the ans to be in this specific format use CONCAT(groupname, '(', value,')' )
you will get some thing like this Wholesalers (3).
This is what I could think off
select fieldname, concat(groupname,' (',
case
when fieldname = 'grp_whs' then
(
select sum(grp_whs) from contacts
)
when fieldname = 'grp_rtl' then
(
select sum(grp_rtl) from contacts
)
when fieldname = 'grp_cns' then
(
select sum(grp_cns) from contacts
)
END
,' )') as groupname
from contact_groups
Check here
http://sqlfiddle.com/#!2/a235f/4
I am assuming you have only 3 fieldname . If there are more than that then I do not think its the best solution , someone may have a better idea on this.
But for your case this should work.
try...
select fieldname ,
case fieldname
when 'grp_whs'
then groupname + '(' + convert(varchar(50),(select count(name) from contacts where grp_whs=1)) + ')'
when 'grp_rtl'
then groupname + '(' + convert(varchar(50),(select count(name) from contacts where grp_rtl=1)) + ')'
when 'grp_cns'
then groupname + '(' + convert(varchar(50),(select count(name) from contacts where grp_cns=1)) + ')'
END as groupname
FROM contact_groups

How do i group for the MAX of a string, while keeping the string instead of its character count?

How do I turn this
+--------+---------------+
| ID | name |
|--------|---------------|
+ 1 | tim |
+-------------------------
| 1 | timothy |
+--------++--------------+
| 1 | timmy |
+--------|---------------|
| 2 | jane +
+--------+---------------+
into this?
+--------+---------------+
| ID | name |
|--------|---------------|
+ 1 | timothy |
+-------------------------
| 2 | jane |
+--------++--------------+
The problem seems to be one of using MAX on a string, while keeping the string and grouping by ID.
FWIW, the table actually has 7K rows and about 40 columns; I don't think that should matter, but I'm mentioning it just in case.
On my existing MAX efforts, I'm getting integers to consolidate, but not strings...
Select Id, Name
From MyTable
Join (
Select Id, Max( Char_Length( name ) ) As NameLen
From MyTable
Group By Id
) As Z
On Z.Id = MyTable.Id
And Z.NameLen = Char_Length( MyTable.Name )
Of course, this will not handle a scenario like Rob, Bob. In that case, both would be returned.