Using multiple SQL Statements to get single result query in PHP - php

I am looking to create 2 teams. After research, I have found that I can sort my table, then assign a "ranking" system in the new sorted order. Now, I am trying to pull just the odd number rows (and even number after I figure out odd).
This statement will do the sorting and ranking:
SELECT t.*, #rownum := #rownum + 1 AS weight FROM golfers t, (SELECT #rownum := 0) r WHERE trip_name_table_ID = 1 ORDER BY golfer_handicap
See SQLfiddle for visual.
So I am creating weight after the sorting. Now, I want to pull my data off of that weight column with this code I found for pulling Odd row (which should now be pulling odd number of weighted sort):
SELECT * FROM golfers WHERE weight IN(SELECT weight FROM golfers WHERE weight%2 <> 0);
How do I use both statements off of each other since weight is an AS column? and what would my
while($rowid = mysqli_fetch_array($result)) {}
look like?

If you want to select only the odd numbered rows from your SQL query then you should use weight%2=1 instead.
The full query should look like this:
SELECT * FROM (
SELECT t.*, #rownum := #rownum + 1 AS weight
FROM golfers t, (SELECT #rownum := 0) r
WHERE trip_name_table_ID = 1
ORDER BY golfer_handicap
) as t1
WHERE weight%2=0
I don't know why you are using while($rowid = mysqli_fetch_array($result)) {} but your mysqli result will contain the exact data that your query returned. You can use foreach instead though.
foreach($result as $row) {
echo $row['golfer_name'];
echo $row['weight'];
}

Related

how can i find second maximum of my query

Here is my SQL query to find a row in currency_price table grouped by maximum date of inserting to table. My question is how to find the second maximum. I mean how can I change this query to find the second maximum row in each group:
select currency_id,buy,sell
from (select * from currency_price order by `currency_id`, cu_date desc,buy,sell) x
group by `currency_id`
with this query i found a row for each id so for example i have sell and buy for each id .exm:
id sell buy
1000 500 480
1001 20 19
...
but here i want the second maximum date for each id.
I know some query to find second maximum but all does not take me to my answer.
If it is MySql then Use LIMIT 1,1; # Retrieve rows [start with rec]1-[fetch rec count]1
http://dev.mysql.com/doc/refman/5.7/en/select.html
Use ROW_NUMBER()
Sample
SELECT * FROM
(
SELECT *,ROW_NUMBER() OVER (ORDER BY AGE DESC) RNUM FROM TEST_TABLE
) QUERY1 WHERE RNUM=2
You could manually add a group order number to your initial ordered query & then select the second from each row.
The inner query, orders as required & numbers the rows starting from 1, resetting each time the currency_id changes.
set #num := 0, #ci := -1;
select currency_id,buy,sell
from
(select *,
#num := if(#ci = currency_id, #num + 1, 1) as gp_number,
#ci := currency_id as dummy
from currency_price
order by `currency_id`, cu_date desc,buy,sell) x
where gp_number=2
This could be put into a stored procedure from workbench as follows :
DELIMITER $$
CREATE PROCEDURE SecondMaximum()
BEGIN
set #num := 0, #ci := -1;
select currency_id,buy,sell
from
(select *,
#num := if(#ci = currency_id, #num + 1, 1) as gp_number,
#ci := currency_id as dummy
from currency_price
order by `currency_id`, cu_date desc,buy,sell) x
where gp_number=2;
END$$
DELIMITER ;
And from PHP you execute "CALL SecondMaximum();"
If you wanted to be able to change tables and/or fields, then you could pass these as string variables to the procedure & create & execute a prepared statement within the stored procedure. Just do a google search for tutorials on those.

MySQL sort by, then sort a selection by

I extract a number of rows from my DB with a query. Let's say that the result is 120 rows. I want to order these rows by 'score'. That's easy, since every row has a field called 'score'. Next I want to order the first 20 rows of the result randomly. So: 1st order by score, then order first 20 randomly. How would I do this?
EDIT: to be clear: I want to show ALL 120 rows, just onder the first 20 of them randomly.
Thanks!
Let's suppose you've got all your results in a PHP array $rows, using something like this SQL:
SELECT * from `table_name` order by `score`;
It sounds like you know how to do that bit, so I omit the details. The bit you want is the following:
// Get the first 20 rows
$top_twenty = array_slice($rows, 0, 20)
// Order them randomly
shuffle($top_twenty);
// Put them back into the $rows array, with their new order
$rows = array_replace($rows, $top_twenty);
Have you tried shuffling the associative array obtained in the result of the SQL?
$stmt = $db->query("SELECT * FROM table ORDER BY score LIMIT 20");
$array = $stmt->fetchAll(PDO::FETCH_ASSOC);
shuffle($array);
foreach($array as $item) {
// Do something
}
SET #rowNum=0;
SELECT #rowNum:=#rowNum+1 AS rowSeq,t.*
FROM tableName t
ORDER BY case WHEN rowSeq < 20 THEN Rand() ELSE score END
Here is a SQL-only (well MySQL-only) solution:
SELECT *
FROM (SELECT *, #rn := #rn + 1 as rn
FROM table cross join
(select #rn := 0) const
ORDER BY score
) t
ORDER BY (rn <= 20) desc,
(case when rn <= 20 then rand() end),
score;

Finding a ranking from a rating field on MySQL

I have a MySQL table that looks like this:
id (int primary)
name (text)
rating (float)
I have a page showing rankings which looks like this:
$i = 0;
$q = mysql_query("SELECT * FROM teams ORDER BY rating DESC");
while($r = mysql_fetch_assoc($q)){
$i++;
print("$i: {$r['name']}<br>");
}
This shows teams in order of their rating, with a ranking. And it works.
Now, if I'm given the ID of a team, how do I find their ranking without running through the loop like this? A single MySQL query which returns the team's info + a numeric ranking indicating how far down the list they would be, if I had rendered the whole list.
Thanks!
To get the ranking you can do:
SELECT COUNT(*) as ranking
FROM teams t
WHERE t.rating >= (SELECT rating FROM teams WHERE id=$ID);
To get all the relevant info too, you can do:
SELECT t.*,COUNT(*) as rank
FROM teams t
JOIN teams t2 ON t.rating<=t2.rating
WHERE t.id=4;
This joins teams to itself joining on t.rating <= t2.rating, and so you get one row for every team that has a rating higher than or equal you.
The COUNT just counts how many teams have a rating higher than or equal to you.
Note that if there's a tie this will give you the lower rank. You can change the <= to a < if you want the highest.
You can also do it this way:
select * from (
select t.*, #rank := #rank + 1 as rank
from (select #rank := 0) as r, t
order by rating desc
) as t
where id = 20

MySQL return every nth record from selected range [duplicate]

I have a series of values in a database that I need to pull to create a line chart. Because i dont require high resolution I would like to resample the data by selecting every 5th row from the database.
SELECT *
FROM (
SELECT
#row := #row +1 AS rownum, [column name]
FROM (
SELECT #row :=0) r, [table name]
) ranked
WHERE rownum % [n] = 1
You could try mod 5 to get rows where the ID is multiple of 5. (Assuming you have some sort of ID column that's sequential.)
select * from table where table.id mod 5 = 0;
Since you said you're using MySQL, you can use user variables to create a continuous row numbering. You do have to put that in a derived table (subquery) though.
SET #x := 0;
SELECT *
FROM (SELECT (#x:=#x+1) AS x, mt.* FROM mytable mt ORDER BY RAND()) t
WHERE x MOD 5 = 0;
I added ORDER BY RAND() to get a pseudorandom sampling, instead of allowing every fifth row of the unordered table to be in the sample every time.
An anonymous user tried to edit this to change x MOD 5 = 0 to x MOD 5 = 1. I have changed it back to my original.
For the record, one can use any value between 0 and 4 in that condition, and there's no reason to prefer one value over another.
SET #a = 0;
SELECT * FROM t where (#a := #a + 1) % 2 = 0;
I had been looking for something like this. The answer of Taylor and Bill led me to improve upon their ideas.
table data1 has fields read_date, value
we want to select every 2d record from a query limited by a read_date range
the name of the derived table is arbitrary and here is called DT
query:
SET #row := 0;
SELECT * FROM ( SELECT #row := #row +1 AS rownum, read_date, value FROM data1
WHERE read_date>= 1279771200 AND read_date <= 1281844740 ) as DT WHERE MOD(rownum,2)=0
If you're using MariaDB 10.2, MySQL 8 or later, you can do this more efficiency, and I think more clearly, using common table expressions and window functions.
WITH ordering AS (
SELECT ROW_NUMBER() OVER (ORDER BY name) AS n, example.*
FROM example ORDER BY name
)
SELECT * FROM ordering WHERE MOD(n, 5) = 0;
Conceptually, this creates a temporary table with the contents of the example table ordered by the name field, adds an additional field called n which is the row number, and then fetches only those rows with numbers which are exactly divisible by 5, i.e. every 5th row. In practice, the database engine is often able to optimise this better than that. But even if it doesn't optimise it any further, I think it's clearer than using user variables iteratively as you had to in earlier versions of MySQL.
You can use this query,
set #n=2; <!-- nth row -->
select * from (SELECT t.*,
#rowid := #rowid + 1 AS ID
FROM TABLE t,
(SELECT #rowid := 0) dummy) A where A.ID mod #n = 0;
or you can replace n with your nth value
SELECT *
FROM (
SELECT #row := #row +1 AS rownum, posts.*
FROM (
SELECT #row :=0) r, posts
) ranked
WHERE rownum %3 = 1
where posts is my table.
If you don't require the row number in the result set you can simplify the query.
SELECT
[column name]
FROM
(SELECT #row:=0) temp,
[table name]
WHERE (#row:=#row + 1) % [n] = 1
Replace the following placeholders:
Replace [column name] with a list of columns you need to fetch.
Replace [table name] with the name of your table.
Replace [n] with a number. e.g. if you need every 5th row, replace it with 5

Mysql Limit column value repetition N times

I have two tables
Customer (idCustomer, ecc.. ecc..)
Comment (idCustomer, idComment, ecc.. ecc..)
obviously the two table are joined together, for example
SELECT * FROM Comment AS co
JOIN Customer AS cu ON cu.idCustomer = co.idCustomer
With this I select all comment from that table associated with is Customer, but now I wanna limit the number of Comment by 2 max Comment per Customer.
The first thing I see is to use GROUP BY cu.idCustomer but it limits only 1 Comment per Customer, but I wanna 2 Comment per Customer.
How can I achieve that?
One option in MySQL is server-side variables. For example:
set #num := 0, #customer := -1;
select *
from (
select idCustomer
, commentText
, #num := if(#customer = idCustomer, #num + 1, 1)
as row_number
, #customer := idCustomer
from Comments
order by
idCustomer, PostDate desc
) as co
join Customer cu
on co.idCustomer = cu.idCustomer
where co.row_number <= 2
This version doesn't require the SET operation:
select *
from (select idCustomer
, commentText
, #num := if(#customer = idCustomer, #num + 1, 1) as row_number
, #customer = idCustomer
from Comments
JOIN(SELECT #num := 0, #customer := 1) r
order by idCustomer, PostDate desc) as co
join Customer cu on co.idCustomer = cu.idCustomer
where co.row_number <= 2
SELECT * FROM Comments AS cm1
LEFT JOIN Comments AS cm2 ON cm1.idCustomer = cm2.idCustomer
LEFT JOIN Customer AS cu ON cm1.idCustomer = cu.idCustomer
WHERE cm1.idComment != cm2.idComment
GROUP BY cm1.idCustomer
However, if you are going to change the number of comments it's better to use Andomar's solution.
There is no need to use cursor, which is very slow. See my answer to Complicated SQL Query About Joining And Limitting. DENSE_RANK will do the trick without all cursor intricacies.
If you are using a scripting language such as PHP to process the results, you could limit the number of results shown per customer after running the query. Set up an array to hold all the results, set up another array to hold the number of results per customer and stop adding the query results to the result set after the count exceeds your limit like so:
$RESULTS = array();
$COUNTS = array();
$limit = 2;
$query = "SELECT customer_id, customer_name, customer_comment FROM customers ORDER BY RAND()";
$request = mysql_query($query);
while ($ROW = mysql_fetch_assoc($request))
{
$c = $ROW['customer_id'];
$n = $COUNTS[$c];
if ($n<$limit)
{
$RESULTS[] = $ROW;
$COUNTS[$c]++;
}
}
This guarantees only two comments per customer will be shown pulled randomly or however you want, the rest gets thrown out. Granted you are pulling ALL the results but this is (probably) faster than doing a complex join.

Categories