I have a table with some hierarchical data in it. I've handled ordering it in a hierarchical way via calculating the path with a trigger but now I wanna sort them with another parameter too.
Take a look at these pictures:
This Is The Table With Hierarchical Data Ordered By Path:
I Expect These Two Rows To Swap Because Row ID = 4 Has A Date Before Row ID = 2:
So How Can I Order Each Level By Date Column?
NOTE:
The ID Is A Random Number Generated By A TRIGGER.
You can use FIND_IN_SET so as to extract the hierarchy level of each row. Then use this in the ORDER BY clause:
SELECT ID, Name, ParentId, Date, Path
FROM mytable
ORDER BY FIND_IN_SET(ID, REPLACE(Path, '.', ',')), Date
Note: We have to use REPLACE function to replace '.' characters with ','
so that FIND_IN_SET works as expected.
Demo here
Alternatively you can modify your trigger so as to generate an additional 'level' field and use this field in the ORDER BY clause of your query.
I think you have to add the date in the path-column on each level, since you cannot simply order by date.
So the path-column should look something like this:
0.date-2015-12-09 22:15:12.parent1.date-2015-12-09 22:15:14.parent4
0.date-2015-12-09 22:15:12.parent1.date-2015-12-09 22:15:17.parent2
In that case, date superceeds the parent-level.
In this situation 1.4 would appear before 1.2, because 1.4 happend before 1.2
The path-column does get a little lengthy, but its the only way you can incorporate your own hierachy, I think
Did I get it this time? :-)
Hope I could help :-)
Related
I have a small problem, I have a table like this:
id|name|group|date_created
1|Volvo|1,3|06-04-2020 10:00:00
2|Audi|3|06-04-2020 10:00:00
etc....
Now I wish I could get all the records that have the value 1 inside the group column.
I tried LIKE "%1%", but I don't think it's a good query. Can you address me?
SELECT id FROM cars WHERE group LIKE '%1%'
The problem with your query is that it would wrongly match '1' against a list like '45,12,5' for example.
One method is to add commas on both ends before searching:
where concat(',', `group`, ',') like '%,1,%';
But in MySQL, it is much more convenient to use string function find_in_set(), whose purpose is just what you are looking for, ie search for a value in a comma-separated list:
select id from cars where find_in_set('1', `group`) > 0
Notes:
you should fix your data model, and have a separated table to store relationship between ids and groups, with each tuple on a separate row. Related reading: Is storing a delimited list in a database column really that bad?
group is a reserved word in MySQL, so not a good choice for a column name (you would need to surround it with backticks everytime you use it, which is error-prone)
I am using the test data "bank" to study mysql on mac. I have a question about the alphabet sort in mysql.
I have a example codeselect cust_id,cust_type_cd,city,state,fed_id from customer order by 2 asc;
The return shows in column 2, "I" is before "B".
Anyone knows what is the reason? Many thanks.
I would guess that cust_type_cd is an ENUM column with "I" ordered before "B" in the enum definition.
Enums sort by the ordinal position of the value in the list defined by the enumeration, not by the alphabetical value.
To sort alphabetically, either define the enum with entries in alphabetical order, or else force the value to be converted to its string value:
... ORDER BY CONCAT(cust_type_cd) ASC
See also http://dev.mysql.com/doc/refman/5.6/en/enum.html#enum-sorting
Note that using a function like that in the ORDER BY clause spoils any chance of using an index for sorting. It will be forced to use a filesort.
Use below Query. It seems there is some space before I character.
select cust_id,trim(cust_type_cd) cust_type_cd,city,state,fed_id from customer order by 2 asc
Using order by column numbers is strictly not recommended. It is especially not used when SELECT * is not used with this. Also it will create problems when somebody alters the table, adds/removes some columns. This link might help you http://blog.sqlauthority.com/2010/12/27/sql-server-order-by-columnname-vs-order-by-columnnumber/
You have to give order by column name in the table
Suppose if you want to sort according to cust_id you have to use
select cust_id,cust_type_cd,city,state,fed_id from customer order by cust_id asc;
I'm looking to do a simple comparison test in MySQL, I have a field that stores ID's like so:
1;2;23;12
Or if only one option was selected when creating that entry it would only be one:
2
At any rate I am looking to filter my select query to find entries in the DB that have for instance the ID 2 in them. So I need some sort of comparison to compare that id 2 against that column with those values and a possible single value.
You are looking for find_in_set function:
Select
*
from
your_table
where
FIND_IN_SET('2',REPLACE( '1;2;23;12', ';' , ',' ) ) > 0
I would recommend to you to normalize database (see: http://en.wikipedia.org/wiki/First_normal_form)
Also, notice to you that this kind of queries don't has high performance.
I have a mysql table in which there is a column e.g. called name. The column data has a specific pattern nameBase+number. E.g.
name
----------
test0
test1
test2
stack0
stack1
stack2
Each time I want to add data to the column, I have to find the last number for specific nambeBase and add the new entry +number+1.
For example, if now test came, I have to add test3 to db.
My question: What is the best way to 1. check if the nameBase already exists in db(sth like contains) and 2.find the last nameBase number. E.g. here for test is 3.
Update : Everyone, one update. I finally used java Pattern class. So cool and easy. It made everything so simple. I just could add the /d to pattern and then I could check if that matches the name and could use the pattern group to easily access the second part.
The real solution here is to change the database schema to split this into two columns, the name and its number. It becomes trivial then to get the aggregate MAX() via
SELECT name, MAX(num) AS num FROM tbl GROUP BY name
However,if changing it is not an option, I would recommend using REPLACE() to remove the name portion from the column value leaving only the number portion when querying, and get the aggregate MAX() of that to find the highest existing number for it:
SELECT
MAX(REPLACE(name, <the name to search>, '')) AS maxnum
FROM tbl
WHERE
name LIKE '<the name to search>%'
Or instead of LIKE, using a regular expression, which is more accurate than LIKE (in case a name contains another name, the LIKE might match) but more expensive:
SELECT
MAX(REPLACE(name, <the name to search>, '')) AS maxnum
FROM tbl
WHERE
name REGEXP '^<the name to search>[0-9]+$'
I would do this with an additional table with two columns and store in this table each name and the last assigned id. And then replace your nameBase+number column in your original table with a name column being a foreign key to the addition table, and a number column, being the appropriate count for that entry.
This will be much easier and more efficient to manipulate.
If possible, I would restructure the table to place these in either 2 tables (better) or at least two columns (medium). The structure you have is not normalized at all :-/
Without knowing too much about your schema; here is my recommendation for the two-table solution: (note: this is normalized and also follows the idiom "Do not store that which can be calculated")
names
------
id | name
01 | test
02 | stack
name_hits
-------
name_id | date
01 | 01/01/2001
01 | 01/15/2001
01 | 04/03/2001
02 | 01/01/2001
...
and then select like this:
SELECT names.name, count(name_hits.id) as hits
FROM names JOIN name_hits ON names.id=name_hits.name_id
GROUP BY names.id
and insert like this:
INSERT INTO name_hits SELECT id, NOW() FROM names WHERE name = "stack";
Presuming that you are unable to change the structure of the table, you can do what you want. However, it is rather expensive.
What you would like to do is something like:
select name
from t
where left(name, length(<name parameter>)) = <name parameter>
order by name desc
limit 1
Unfortunately, your naming probably does not allow this, because you are not left padding the numeric portion with zeroes.
So, the following gets around this:
select name,
cast(substring(name, length(<name parameter>), 1000) as int) as number
from t
where left(name, length(<name parameter>)) = <name parameter>
order by 2 desc
limit 1
This is not particularly efficient. Also, indexes cannot really help with this because the collating sequence for strings is different than for numbers (test0, test1, test10, test100, test11, etc. versus 0, 1, 2, 3, 4 . . .).
If you can, I would follow the advice of the others who suggest multiple columns or tables. I only offer this as a method where you don't have to modify the current table.
If you cannot change the schema, try this:
INSERT INTO names (name)
SELECT CONCAT("stack", CAST(TRIM(LEADING "stack" FROM name) AS INT)+1)
WHERE name LIKE "stack%" ORDER BY name DESC LIMIT 1;
The idea is:
select the "highest" previous value,
chop of the name,
cast the remaining string as an int,
add one to it,
then put the name back on it.
I have not tested this... I hope it leads you in the right direction.
Note that I have used a constant string "stack" as an example, you will likely want to make that dynamic.
I'm dealing with a legacy database table that has no insertion date column or unique id column, however the natural order of insertion is still valid when examined with a simple SELECT * showing oldest to newest.
I'd like like to fetch that data with pagination but reverse the order as if it was ORDER BY date DESC
I've thought about wrapping the query, assigning a numeric id to the resulting rows and then do an ORDER BY on the result but wow that seems crazy.
Is there a more simple solution I am overlooking?
I cannot add columns to the existing table, I have to work with it as is.
Thanks for any ideas!
Use #rownum in your query to number each row and then order by the #rownum desc. Here's an example.
select #rownum:=#rownum+1 ‘rank’, p.* from player p, (SELECT #rownum:=0) r order by score desc limit 10;
Finally, beware that relying on the current order being returned long-term isn't recommended.
If you're writing an application to process the data, another approach might be to run your current query, then iterate over the returned records from last to first.
If you have too many records, then you may wish to instead use a view. This is a Database object which can be used to combine data from different tabls, or present a modified view of a single table, amongst other things. In this case, you could try creating a view of your table and add a generated ID column. You could then run SELECT statements against this view ordering by the new column you have added.
However be aware of the advice from another poster above: the order in which rows are returned without an ORDER BY clause is arbitrary and may change without notification. It would be best to amend your table if at all possible.
mySQL CREATE VIEW syntax