I'm Using PHP and MySQL to show some data on the screen, but I need to order rows of a table using 2 columns.
On one column Restricted
I have the following json (["PT","GB"])
On the other column rate I have decimal values from 0 to 10
What I'm looking is for a way to order first Restriced contains GB Then rate DESC (If I can use only mysql would be the best)
Current SQL at the moment is
SELECT * FROM Partner ORDER BY FIELD (restricted, '%GB%'), FIELD rate DESC
I've searched the web for a solution, I've been at this for almost a month, no solution to be found
** EDIT **
How it should look
Partner Name | Countries | rate |
AAAAA | ["GB","FR","PT"] | 9.0 |
BBBBB | ["GB","FR","PT"] | 8.8 |
CCCCC | ["GB","FR","PT"] | 7.2 |
DDDDD | ["US","FR","PT"] | 9.0 |
EEEEE | ["US","FR","PT"] | 8.8 |
FFFFF | ["US","FR","PT"] | 7.2 |
You need to use below mentioned steps:
Break your records in 2 Parts (Having 'GB' and not Having 'GB').
Sort both record-sets separately as per you logic
join both records using UNION ALL
so you query should be as below:
(SELECT * FROM partner where restricted like '%GB%' ORDER BY restricted,
rate DESC)
union all
(SELECT * FROM partner where restricted not like '%GB%' ORDER BY restricted,
rate DESC)
Related
In one of my recent PHP (CodeIgniter) based projects, a MySQL table contains almost 30.000 records (and will increase more). If I show those records as a list, the performance is reduced (MySQL retrieves all of those records in a quick time, but running a PHP loop for around 30.000 times is not feasible). So my plan is to load 100 records at a time and create a pagination so that the user can select a page number to load that specified number of records.
I can load the first 100 records easily using the following query:
SELECT * FROM <table_name> ORDER BY id ASC LIMIT 100
But the problem is: when I select page=2, page=3, ...., page=n. How can I write a MySQL query that will load 2nd 100 records (for page=2), 3rd 100 records (for page=3),... ... nth 100 record (for page=n)?
N.B: Please refer the 101 number record may not have the id 101, similar 201 number record may not have the id of 201, so please don't suggest me a query that will depend on Primary Key
Can anyone help me to find out the correct query?
Thanks
first hundred
SELECT * FROM <table_name> ORDER BY id ASC LIMIT 0, 100
next hundred
SELECT * FROM <table_name> ORDER BY id ASC LIMIT 100, 100
you're very observant about putting the order by in
THE LIMIT STATEMENT EXPLAINED:
The LIMIT statement is NOT a WHERE clause. It does not select by id nor in fact by any criteria, (there where clause does that) Instead the limit clause simply ensures that you are returned a piece of the block of results that are subset of "everything". Hence why it is important to mention an order by each time, so that each subsequent call will give you the correct piece of the data block in order, and you can 'next', 'next', 'next' through them
EG: for the disordered table this_table:
+-------+-------------+
| id | value |
+-------+-------------+
| 1 | bob |
| 12 | fish |
| 112 | pink |
| 2 | cat |
| 8 | dog |
| 56 | blue |
| 88 | grey |
| 87 | red |
+-------+-------------+
the selects return as below:
SELECT * FROM <this_table> ORDER BY id ASC LIMIT 0,5
+-------+-------------+
| id | value |
+-------+-------------+
| 1 | bob |
| 2 | cat |
| 8 | dog |
| 12 | fish |
| 56 | blue |
+-------+-------------+
and
SELECT * FROM <this_table> ORDER BY id ASC LIMIT 5,5
+-------+-------------+
| id | value |
+-------+-------------+
| 87 | red |
| 88 | grey |
| 112 | pink |
+-------+-------------+
notice the lack of rows 9 and 10 this is deliberate and shows MySQL working as intended
incidentally you should also look at adding an index on id this will MASSIVELY increase the speed of these selects
ALTER TABLE <table_name> ADD INDEX `id` (`id`)
try this
SELECT * FROM <table_name> ORDER BY id ASC LIMIT <p * 100>, 100
so u must have
SELECT * FROM <table_name> ORDER BY id ASC LIMIT 0, 100
SELECT * FROM <table_name> ORDER BY id ASC LIMIT 100, 100
this work if first page = 0, else, u need (p-1) * 100
https://www.w3schools.com/php/php_mysql_select_limit.asp
I am currently examining aerospike for replacing my company MySQL database. Currently, in MySQL, we have a table that stores the transaction data, the table looks like this :
+--------+------------+-----------+------------+-----+--------+
| trx_id | trx_date | client_id | product_id | qty | total |
+--------+------------+-----------+------------+-----+--------+
| 1 | 2015-01-01 | 1 | 1 | 100 | 100000 |
| 2 | 2015-01-02 | 2 | 2 | 200 | 200000 |
| 3 | 2015-01-03 | 3 | 3 | 300 | 300000 |
+--------+------------+-----------+------------+-----+--------+
For reporting, we usually do something like :
SELECT MONTH(trx_date), SUM(qty), SUM(total) FROM transaction WHERE client_id = 1 AND product_id = 1 GROUP BY MONTH(trx_date)
to get the monthly transaction data for a client.
I've read the documentation for the Aerospike PHP client and I don't seem to find anything similar to AND, GROUP BY, or MONTH.
So, in Aerospike PHP client, what is the recommended way to achieve something like that?
Thanks.
Aerospike is a NoSQL key-value store, and as such you can't expect to use SQL with it. However, using Lua as the User-Defined Function (UDF) language, you can extend the basic functionality.
What you are looking for is an aggregation, applying a stream UDF to the results of a query.
There is an example of implementing a GROUP BY x HAVING in the PHP client's documentation for the aggregate() method. The thing to remember is that you want the secondary index query to eliminate as many records as you can, so that predicate should used for the 'WHERE', and the secondary filtering for the 'AND' should happen inside the stream UDF's filter on the smallest possible data set.
Reading the UDF Development Guide would also help.
I've searched for a few hours now, but couldn't find relative solution to a specific algorithm I am working on. To simplify the obstacle, I would like to present the information in just one table.
_____________________________________
| User | Item | price | qty |
-------------------------------------
| Annie | Dress | 80 | 1 |
| Bob | Jeans | 65 | 3 |
| Cathy | Shoes | 60 | 4 |
| David | Shirts | 40 | 6 |
| Annie | Shoes | 60 | 2 |
| Bob | Shirts | 55 | 2 |
| Cathy | Jeans | 65 | 1 |
| David | Ties | 20 | 5 |
-------------------------------------
Problem # 1: Show users whose total price for shopping at the store is 300 or more and quantity of their purchase is less than or equal to 3. These shoppers will be mailed a coupon for $40.
Problem # 2: Show users whose total qty is greater than or equal to 7 and the total for price is 275 or more. These shoppers will be mailed a coupon for $20.
The rows within the table are not transaction specific. The table can represent separate transactions within a month. We're just trying to find certain returning customers who we would like to reward for shopping with us.
I'm not sure if this can be done only via MySQL, or if I need to have separate queries and store rows into arrays and compare them one by one.
What I have tried so far are the followings:
SELECT * FROM table where SUM(price) as Total >= 300 AND SUM(qty) <=3;
I've also tried the following after the research:
SELECT SUM(price) as Total FROM table WHERE SUM(qty) <=3;
I keep getting syntax errors in MySQL shell. You don't have to solve the problems for me, but if you can guide me through the logic on how to solve the problems, I'd appreciate it very much.
Lastly I'd like to ask once, can I solve this with only MySQL or do I need to store the rows into PHP arrays and compare each indexes?
You can't use an aggregate function in the WHERE clause, you have to use HAVING. WHERE operates on individual rows during the selection, HAVING operates on the final results after aggregating.
SELECT *, SUM(price*qty) as Total
FROM table
GROUP BY user
HAVING Total >= 300 AND SUM(qty) <= 3
SUM is an aggregate function, meaning it applies to a group of clubbed rows. S say i am grouping the table data based on NAME then sum function would sum all the price of one NAME.
Having said this, if you think logically it would not make any sense to put the sum(price) in a WHERE clause because where clause would not know which SUM(PRICE) for which NAME to operate on(where clause operates only after a temporary view has been generated).
So we have the HAVING clause in SQL. This is used to compare the results of aggregrate function at each step of aggregation.
Consider it like this:
In where clause, when the ANNIE row from your DB is returned, it does not know what SUM(PRICE) means.
While in HAVING clause the SUM(PRICE)>300 condition is executed only when SQL has finished grouping all the ANNIE data into one group and calculated the SUM(PRICE) for her.
For question 1:
SELECT USER, SUM(PRICE)
FROM table
GROUP BY user
HAVING SUM(PRICE) >= 300 AND SUM(QTY) <= 3
For Question 2:
SELECT USER, SUM(PRICE)
FROM table
GROUP BY user
HAVING SUM(PRICE) >= 275AND SUM(QTY) >=7
I have a table with scores like this:
score | user
-------------------
2 | Mark
4 | Alex
3 | John
2 | Elliot
10 | Joe
5 | Dude
The table is gigantic in reality and the real scores goes from 1 to 25.
I need this:
range | counts
-------------------
1-2 | 2
3-4 | 2
5-6 | 1
7-8 | 0
9-10 | 1
I've found some MySQL solutions but they seemed to be pretty complex some of them even suggested UNION but performance is very important. As mentioned, the table is huge.
So I thought why don't you simply have a query like this:
SELECT COUNT(*) as counts FROM score_table GROUP BY score
I get this:
score | counts
-------------------
1 | 0
2 | 2
3 | 1
4 | 1
5 | 1
6 | 0
7 | 0
8 | 0
9 | 0
10 | 1
And then with PHP, sum the count of scores of the specific ranges?
Is this even worse for performance or is there a simple solution that I am missing?
Or you could probaly even make a JavaScript solution...
Your solution:
SELECT score, COUNT(*) as counts
FROM score_table
GROUP BY score
ORDER BY score;
However, this will not returns values of 0 for count. Assuming you have examples for all scores, then the full list of scores is not an issue. You just won't get counts of zero.
You can do what you want with something like:
select (case when score between 1 and 2 then '1-2'
when score between 3 and 4 then '3-4'
. . .
end) as scorerange, count(*) as count
from score_table
group by scorerange
order by min(score);
There is no reason to do additional processing in php. This type of query is quite typical for SQL.
EDIT:
According to the MySQL documentation, you can use a column alias in the group by. Here is the exact quote:
An alias can be used in a query select list to give a column a
different name. You can use the alias in GROUP BY, ORDER BY, or HAVING
clauses to refer to the column:
SELECT
SUM(
CASE
WHEN score between 1 and 2
THEN ...
Honestly, I can't tell you if this is faster than passing "SELECT COUNT(*) as counts FROM score_table GROUP BY score" into PHP and letting PHP handle it...but it add a level of flexibility to your setup. Create a three column table as 'group_ID', 'score','range'. insert values into it to get your groupings right
1,1,1-2
1,2,1-2
1,3,3-4
1,4,3-4
etc...
Join to it on score, group by range. THe addition of the 'group_ID' allows you to set groups...maybe have group 1 break it into groups of two, and let a group_ID = 2 be a 5 set range (or whatever you might want).
I find the table use like this is decently fast, requires little code changing, and can readily be added to if you require additional groupings or if the groupings change (if you do the groupings in code, the entire case section needs to be redone to change the groupings slightly).
How about this:
select concat((score + (1 * (score mod 2)))-1,'-',(score + (1 * (score mod 2)))) as score, count(*) from TBL1 group by (score + (1 * (score mod 2)))
You can see it working in this fiddle: http://sqlfiddle.com/#!2/215839/6
For the input
score | user
-------------------
2 | Mark
4 | Alex
3 | John
2 | Elliot
10 | Joe
5 | Dude
It generates this:
range | counts
-------------------
1-2 | 2
3-4 | 2
5-6 | 1
9-10 | 1
If you want a simple solution which is very powerful, add an extra field within your table and put a value in it for the score so 1 and 2 have the value 1, 3 and 4 has 2. With that you can group by that value. Only by inserting the score you've to add an extra field. So your table looks like this:
score | user | range
--------------------------
2 | Mark | 1
4 | Alex | 2
3 | John | 2
2 | Elliot | 1
10 | Joe | 5
5 | Dude | 3
Now you can do:
select count(score),range from table group by range;
This is always faster if you've an application where selecting has prior.
By inserting do this:
$scoreRange = 2;
$range = ceil($score/$scoreRange);
Right now I have a PHP script that is fetching the first three results from a MYSQL database using:
SELECT * FROM table Order by DATE DESC LIMIT 3;
After that command I wanted PHP to fetch the next three results, initially I was going to use:
SELECT * FROM table Order by DATE DESC LIMIT 3,3;
However there will be a delay between the two commands which means that it is very possible that a new row will be inserted into the table during the delay. My first thought was to store the DATE value of the last result and then include a WHERE DATE > $stored_date but if entry 3 and 4 have the same date it will skip entry 4 and return results from 5 onward. This could be avoided using the primary key field which is an integer which increments automatically.
I am not sure which the best approach is, but I feel like there should be a more elegant and robust solution to this problem, however I am struggling to think of it.
Example table:
-------------------------------------------
| PrimaryKey | Data | Date |
-------------------------------------------
| 0 | abc | 2014-06-17 11:43:00 |
| 1 | def | 2014-06-17 12:43:00 |
| 2 | ghi | 2014-06-17 13:43:00 |
| 3 | jkl | 2014-06-17 13:56:00 |
| 4 | mno | 2014-06-17 14:23:00 |
| 5 | pqr | 2014-06-17 14:43:00 |
| 6 | stu | 2014-06-17 15:43:00 |
-------------------------------------------
Where Data is the column that I want.
Best will be using primary key and select like
SELECT * FROM table WHERE pk < $stored_pk Order by DATE DESC LIMIT 3;
And if you have automatically generated PK you should use ORDER BY pk it will be faster
Two options I can think of depending on what your script does:
You could either use transactions: performing these queries inside a transaction will give you a consistent view of the data.
Alternatively you could just use:
SELECT * FROM table Order by DATE DESC;
And only fetch the results as you need them.