MySQL sort within one column of data - php

For example, if I had a column in a database called RANDOM that has random bits of information distinguished by their end notation like this:
RANDOM
1. 12312 KM, 201 M, 1213 H, 101029 DOLLARS
2. 231 KM, 2351 M, 754 H, 345 DOLLARS, 120 L, 1201 FT
3. 2324 M
Some entries have other miscellaneous but important data points while others my only have one or two.
I would like to sort using only data within column RANDOM.
$RESULT = mysqli_query($CON, "SELECT * FROM TABLE WHERE RANDOM CONTAINS 'M' ORDER BY 'NUMBER BEFORE M'");
Therefore this would find the 3 rows that contain 'M' and then sort by the number in front of 'M'. Similarly with other variables like KM or DOLLARS. Is this possible using pure MySQL in a single statement?

Your sort would require a 2 aspirin headache with
ORDER BY substr('str',locate(str,a,b),locate(str,b,c))

If I understand the question then yes you could do it in one line , just requires some manipulation as your data is all in the one column.
SELECT * FROM
(SELECT *, CAST(LEFT(RANDOM, INSTR(RANDOM, '.') - 1) AS UNSIGNED) AS ORD_NUMBER
FROM `test_table`
) DERIVED_TABLE ORDER BY ORD_NUMBER DESC
Basically we are getting the number at the start by using INSTR to find the first . ( assuming this is the format for all items) and then to be safe we cast that as an integer. The data is returned using a derived table so then we sort on our dynamically calculated ord_number that's the result from filtering. Hope that helps.
So using this principle you can filter out however you like :)
But maybe rethink the db Design as doing anykind of query like this means maybe your RDBMS isn't setup correct (simple solution new column with that value :))

Related

Finding Interval of a data present on latest 2 dates

I'm developing a web-based tool that can help analyze number intervals that occurs in a 6-digit lottery.
Let us focus on a certain number first. Say 7
The sql query I've done so far:
SELECT * FROM `l642` WHERE `1d`=7 OR `2d`=7 OR `3d`=7 OR `4d`=7 OR `5d`=7
OR `6d`=7 ORDER BY `draw_date` DESC LIMIT 2
This will pull the last two latest dates where number 7 is present
I'm thinking of using DATEDIFF but I'm confused on how to get the previous value to subtract it on the latest draw_date
My goal is to list the intervals of numbers 1-42 and I'll plan to accomplish it using PHP.
Looking forward to your help
A few ideas spring to mind.
(1) First, since you perfectly have your result set ordered, use PHP loop on the two rows getting $date1 =$row['draw_date']. Then fetch next/last row and set $date2 =$row['draw_date']. With these two you have
$diff=date_diff($date1,$date2);
as the difference in days.
(2)
A second way is to have mysql return datediff by including a rownumber in the resultset and doing a self-join with aliases say alias a for row1 and alias b for row2.
datediff(a.draw_date,b.drawdate).
How one goes about getting rownumber could be either:
(2a) rownumber found here: With MySQL, how can I generate a column containing the record index in a table?
(2b) worktable with id int auto_increment primary key column with select into from your shown LIMIT 2 query (and a truncate table worktable between iterations 1 to 42) to reset auto_increment to 0.
The entire thing could be wrapped with an outer table 1 to 42 where 42 rows are brought back with 2 columns (num, number_of_days), but that wasn't your question.
So considering how infrequent you are probably doing this, I would probably recommend not over-engineering it and would shoot for #1

mysql - getting only the results with diferences from same table

So I have a single table inside which I have a score system for points. It looks something along this line:
Columns:
ID Name Date Points
1 Peter 2014-07-15 5
2 John 2014-07-15 6
3 Bill 2014-07-15 3
and so on...
Everyday, the new results are being put into the table with the total amount of points acumulated, however in order to be able to get historic values, the results are put into new rows. So on the 2014-07-16, the table will look like this:
ID Name Date Points
1 Peter 2014-07-15 5
2 John 2014-07-15 6
3 Bill 2014-07-15 3
4 Peter 2014-07-16 11
5 John 2014-07-16 12
6 Bill 2014-07-16 3
However sometimes when a player doesn't take part for the whole day and doesn't get any points, he will still be added, but the points will remain the same (here this is shown by the case of Bill).
My question is how to count the number of each type of players (active - Peter and John ie when the points value changes from one date to another and inactive - Bill ie when the points value stays the same).
I have managed to get this query to only select players who do have the same value, but it's giving me the list of players rather than the count. Although I could potentialy be wrong with this query:
SELECT Points, name, COUNT(*)
FROM points
WHERE DATE(Date) = '2014-07-15' OR DATE(Date) = '2014-07-16'
GROUP BY Points
HAVING COUNT(*)>1
I'm not sure how to count the number of rows (could do a bypass trick with PHP getting the number of rows, but interested in SQL only) or how to invert it, to get a count of players who have a different score (again, could get total of rows and then subtract the above number, but not interested in that either - I'd prefer the SQL).
Regards and thanks in advance.
You are pretty close.
If you have at most one row per "player" per "date", you could do something like this:
SELECT SUM(IF(c.cnt_distinct_points<2,1,0)) AS cnt_inactive
, SUM(IF(c.cnt_distinct_points>1,1,0)) AS cnt_active
FROM ( SELECT p.name
, COUNT(DISTINCT p.points) AS cnt_distinct_points
FROM points p
WHERE DATE(p.Date) IN ('2014-07-15','2014-07-16')
GROUP BY p.name
) c
The inline view query (aliased as c) gets a count of the distinct number of "points" values for each player. We need to "group by" name, so we can get a distinct list of players, along with an indication whether the points value was different or not. If all of the non-NULL "points" values for a given player are the same, COUNT(DISTINCT ) will return a value of 1. Otherwise, we'll get a value larger than 1.
The outer query processes that list, collapsing all of the rows into a single row. The "trick" is to use expressions in the SELECT list that return 1 or 0, depending on whether the player is "inactive", and perform a SUM aggregate on that. Do the same thing, but a different expression to return a 1 if the player is "active".
If the count of distinct points for a player is 1, we'll essentially be adding 1 to cnt_inactive. Similarly, of the distinct points for a player is greater than 1, we'll be adding 1 to the cnt_active.
If this doesn't make sense, let me know if you have questions.
NOTE: Ideally, we'd avoid using the DATE() function around the p.Date column reference, so we could enable an appropriate index.
If the Date column is defined as (MySQL datatype) DATE, then the DATE() function is unnecessary. If the Date column is defined as (MySQL datatype) DATETIME or TIMESTAMP, we could use an equivalent predicate:
WHERE p.Date >= '2014-07-15' AND p.Date < '2014-07-16' + INTERVAL 1 DAY
That looks more complicated, but a predicate of that form is sargable (i.e. MySQL can use an index range scan to satisfy it, rather than having to look at every row in the table.)
For performance, we'd probably benefit from an index with leading columns of name and date
... ON points (`name`,`date`)
(MySQL may be able to avoid a "Using filesort" operation for the GROUP BY).
I would solve this problem by looking at the previous number of points and then doing a comparison:
select date(date), count(*) as NumActives;
from (select p.*,
(select p2.points
from points p2
where p2.name = p.name and p2.date < p.date
order by p2.date desc
limit 1
) as prev_points
from points p
) p
where prev_points is NULL or prev_points <> points;
Of course, you can add a where clause to get the count for any particular day.

How to get the available area codes?

This is a pretty complex MySQL query for me.
what i have is a table of phone numbers in the format of a US phone number 8153216458.
Example:
TABLE: numbers(number)
4512163215
4512158211
4512110579
8812163215
9405462136
3021548641
What i want is to list the available area codes ( as i'm selling numbers ) without repeating them, Some query that is based on the first 3 digits & finally ordered correctly.
Output:
302
451
881
940
Any solution? i don't mind if it's still using php manipulation.
Thanks
Try this:
select distinct substring(number, 1, 3)
from numbers;
Or, as mentioned by Jeff in the comments, left is also an option:
select distinct left(number, 3)
from numbers;
Check out documentation for string functions on MySQL.
something like this?
select distinct substring(number, 0, 3) as 'number'
from numbers
order by number

Sort MySQL in Human

here's the code:
$sql_namesResult = mysql_query("SELECT name FROM `scrimaprovedlist` ORDER BY `scrimaprovedlist`.`eorank`");
eo rank is a NUMERICAL value for a rank (general, colonel, ect).
The problem is, when i set myself for 1, i am the top, but comes rank 10, instead of rank 2. how do i edit this to make it show in order:
1
2
3
10
20
30
I'm currently using "rank" instead of "eorank" because it is easier. but the problem is i have to manually edit the ranks over and over again so that they show in the correct order. Any ideas?
Viewable at http://www.thexcrew.com/modules.php?name=Roster
ORDER BY CAST(scrimaprovedlist.eorank AS INTEGER)
Your ranks are strings instead of integers so they will be sorted as a string unless you cast or convert them to integers which I've done above
figured out a way, i changed my Numerical value to an alphabetical value. using only 17 ranks, i am able to substitute #'s for letters. thanks for the help anyway :)

how to calculate the network diameter

I have data stored in relational database mysql and PHP.
I have a table called "rel" which has two fields:
from_node | to_node
=====================
1 2
1 3
2 3
and so on......
How can I calculate the network Diameter of a network. I know it is the longest or shortest path between any two pairs but how do I calculate it?
Is there any PHP script or function which can help me to do it?
Assuming you have a connected graph (otherwise the max distance is infinite) and all your node points are numbers....
Seed a table (from, to, distance) with (from_node, to_node, 1). For each tuple, you must ensure that the value of from_node is always less than the value of to_node
CREATE TABLE hops (
from_node int not null,
to_node int not null,
distance int not null default 0,
primary key (from_node, to_node, distance)
)
-- first load:
INSERT INTO hops (from_node, to_node)
SELECT from_node, to_node FROM rel;
-- iterative step
INSERT INTO hops (from_node, to_node, distance)
SELECT a.from_node, b.to_node, min(a.distance+b.distance)
FROM hops a, hops b
WHERE a.to_node = b.from_node
AND a.from_node <> b.from_node -- not self
AND a.to_node <> b.to_node -- still not self
AND a.from_node <> b.from_node -- no loops
AND NOT EXISTS ( -- avoid duplicates
SELECT * FROM hops c
WHERE c.from_node = a.from_node
AND c.to_node = b.to_node
AND c.distance = a.distance+b.distance)
GROUP BY a.from_node, b.to_node
Execute the insert repeatedly until no rows are inserted. Then select max distance to get your diameter.
EDIT: For a graph with weighted vertices, you would just seed the distance field with the weights rather than using 1.
See the Wikipedia article on graph (network) terms related to distance and diameter. It mentions a few notes on how to find the diameter. This section of the article on connected components of graphs also suggests an algorithm to discover these connected components, which could be adapted very easily to tell you the diameter of the graph. (If there are more than one components then the diameter is infinite I believe.) The algorithm is a simple one based on a bread/depth-first search, so it shouldn't be much trouble to implement, and efficiency shouldn't be a great problem either.
If you're not up to writing this (though I don't think it would take that much effort), I recommend looking for a good network/graph analysis library. There's a few out there, though I'm not sure which ones you'd want to look at using PHP. (You'd probably have to use some sort of interop.)
Hope that helps, anyway.
I really think you meant you wanted to find the cluster coefficient of a network. Furthermore, you'd like to do it in PHP. I don't know how many good network analysis libraries have been ported to PHP extensions.
However, if you follow the article, it should not be (too) difficult to come up with your own. You don't need to produce pretty graphs, you just have to find the coefficient.
If that's not what you meant, please update / clarify your question.
Network is a connected graph. So why don't you try to build some graph representation from your data and perform BFS or DFS on this? You will get exactly that you are looking for.
That's simple:
Prepare
Add a colum named distance
Give all nodes the distance of -1
First Iteration
Pick any node (e.g. the first)
give it the distance of 1
Now iterate until there are nodes with distance -1
UPDATE table SET distance=:i+1 WHERE from_node IN (SELECT to_node FROM table WHERE distance=:i)
Second Iteration
Pick a node that has the maximum distance (any) - remember it
Set all distances back to -1
Set your remebered node to 1
Call the iteration a second time
This time the maximum distance is the diameter of your graph/network.
In your example you show that each node links to every other node. If this is the case throughout your setup, then the diameter is 1.
If your setup is a line like so in a linear formation:
n=1, n = 2, n = 3, ... n
Then your diameter is (n+1)/3.
If your setup is more irregular, with a series of N number nodes and K number of links, then your diameter is at least logN/LogK
Edit: To clarify, I'm calculating the average shortest distance between pairs of nodes.
n1 - n2 - n3
(n+1)/3 = 4/3
n1-n2 = 1 hop
n2 - n3 = 1 hop
n1- n2 - n3 = 2 hops
(1+1+2)/3 = 4/3

Categories