Select n Rows Before and After Alphabetical Placement of Search Term - php

Dilemma: I have a table with several hundred rows in it. I would like to submit a mysql_query with a specific search term wherein the query finds where the term would be located alphabetically in the column and then returns the n rows before the placement and n rows after the placement.
Example: Imagine that I have a column like the following (I'm placing it horizontally for the sake of space, but for sake of argument, let's pretend that this is a vertical list of column values):
|apple|asus|adder|billy|cat|dog|zebra|computer|mouse|cookie|donkey|
If I were to run the query on the term courage, assuming n = 3, I would like to have it return the following rows in this order:
|cat|computer|cookie|dog|donkey|mouse|
Alphabetically, the word courage would land right in the middle of those results and we are met with the preceding 3 entries and the following 3 entries.
Language Notes: I'm using php and mysql. I don't have any code to display because I'm not sure whether this needs to be in the where clause, or if it requires a subquery, or if you need to do something with the variable in php before handing it to the query.

You might try an UNION of two SELECTS. Or do it manually.
SELECT term FROM table WHERE term < 'courage' ORDER BY term DESC LIMIT 3;
will return cookie, computer and cat (in descending order).
Then
SELECT term FROM table WHERE term >= 'courage' ORDER BY term ASC LIMIT 3;
will return dog, donkey and mouse.
In PHP, you get the two sets, reverse the first and join.
A wholly-SQL solution might be
SELECT term FROM (
SELECT term FROM table WHERE term < 'courage' ORDER BY term DESC LIMIT 3 )
UNION
SELECT term FROM table WHERE term >= 'courage' ORDER BY term ASC LIMIT 3 )
) ORDER BY term;

Related

MYSQL count rows instead of showing results

So I have the following query, which I use it to get some analytics stats.
SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only order by created asc
This works, and it gives me this:
So what I actually need is, the total count of the rows in this case is 20, this is a dummy example, but I need to use this count to check before showing the stats if the data is too big to be displayed on a graphic.
Can this be achieved?
//LE
So the process will be like this:
1. Get a count of the total rows, if the count of rows is smaller than X(number will be in config and it will be a basic if statement), then go ahread and run the above query.
More info:
I actually use this query to display the stats, I just need to adapt it in order to show the total count rows
So the result of thquery should be
total | 20 in this case
I think you would want to use a derived table. Just wrap your original query in parenthesis after the FROM and then give the derived table an alias (in this case tmp). Like so:
SELECT count(*) FROM (
SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only order by created asc
) as tmp;
If I understand what you want to do correctly, this should work. It should return the actual number of results from your original query.
What's happening is that the results of the parenthesized query are getting used as a sort of virtual table to query against. The parenthesized query returns 20 rows, so the "virtual" table has 20 rows. The outer count(*) just counts how many rows there are in that virtual table.
Based on the PHP tag, I assume you are using PHP to send the queries to MySQL. If so, you can use mysqli_num_rows to get the answer.
If your query result is in $result then:
$total = mysqli_num_rows($result);
Slightly different syntax for Object Oriented style instead of procedural style.
The best part is you don't need an extra query. You perform the original query and get mysqli_num_rows as an extra without running another query. So you can figure out pagination or font size or whatever and then display without doing the query again.
This is an small query but works fine, and give me the total number of rows, you just need add your conditions.
SELECT COUNT(*) FROM table WHERE field LIKE '%condition%'
The group by I think you need to eliminated, becouse, this instead of count the records, divide in all your group by, example: records = 4, with group by you have
1
1
1
1
I hope this help you
You can try this way .
SELECT COUNT(*) FROM ( SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only HAVING total >=20 ) temp

Get previous 10 row from specific WHERE condition

Im currently working on a project that requires MySql database and im having a hard time constructing the query that i want get.
i want to get the previous 10 rows from the specific WHERE condition on my mysql query.
for example
My where is date='December';
i want the last 10 months to as a result.
Feb,march,april,may,june,july,aug,sept,oct,nov like that.
Another example is.
if i have a 17 strings stored in my database. and in my where clause i specify that WHERE strings='eyt' limit 3
Test
one
twi
thre
for
payb
six
seven
eyt
nayn
ten
eleven
twelve
tertin
fortin
fiftin
sixtin
the result must be
payb
six
seven
Thanks in advance for your suggestions or answers
If you are using PDO this is the right syntax:
$objStmt = $objDatabase->prepare('SELECT * FROM calendar ORDER BY id DESC LIMIT 10');
You can change ASC to DESC in order to get either the first or the last 10.
Here's a solution:
select t.*
from mytable t
inner join (select id from mytable where strings = 'eyt' order by id limit 1) x
on t.id < x.id
order by t.id desc
limit 3
Demo: http://sqlfiddle.com/#!9/7ffc4/2
It outputs the rows in descending order, but you can either live with that, or else put that query in a subquery and reverse the order.
Re your comment:
x in the above query is called a "correlation name" so we can refer to columns of the subquery as if they were columns of a table. It's required when you use a subquery as a table.
I chose the letter x arbitrarily. You can use anything you like as a correlation name, following the same rules you would use for any identifier.
You can also optionally define a correlation name for any simple table in the query (like mytable t above), so you can refer to columns of that table using a convenient abbreviated name. For example in t.id < x.id
Some people use the term "table alias" but the technical term is "correlation name".

MYSQL Query: 3 Table UNION, Last 100 Results in ASC Order

So far, I have taken 3 tables and joined them together. What I want to do is display the Last 100 entries (DESC) in ASC ORDER according to the timestamp in the column Posted.
This is as far as I could get: http://sqlfiddle.com/#!9/e2771/1
In addition, if there is a more efficient way to do this in PhP and not MYSQL, I'm all for that. I've tried looking, but haven't been able to find anything that works.
You just need one more level of sort:
select t.*
from (<your query here>) t
order by posted;

How can i sort a mysql result by a list of prioritised ids?

I have a large products database, and every sunday, my script is activating 50 new products.
the thing is, based on some rules in my script, im finding some products i should prioritize activating. (for your example, lets say it should always prioritize activating products with the name 'hello kitty' and 'meshuggah' in them, before everything else)
my db table contains info about the product, and unique id.
lets say that the script finds 5 of these ids, having hello kitty in the title.
so, how can i make the query, so that the prioritized items will come on top, and then followed by id desc?
here some pseudocode of what im after:
SELECT * FROM products order by (id='59', id='47', id='28', id='29', id='20'), id desc limit 50
the result should give me id 59, 47, 28, 29 and 20 first, then followed by other ids in the products table sorted by id descending.
is that possible in one query?
use FIELD
SELECT *
FROM products
order by FIELD(id,59,47,28,29,20), id desc
limit 50
Ordering by specific field values with MySQL
Actually, your pseudo code also comes pretty close to such a query:
SELECT *
FROM products
order by id='59' desc, id='47' desc, id='28' desc, id='29' desc, id='20' desc, id desc
limit 50
This uses the fact that a condition evaluates to 0 or 1.

Getting random results from large tables

I'm trying to get 4 random results from a table that holds approx 7 million records. Additionally, I also want to get 4 random records from the same table that are filtered by category.
Now, as you would imagine doing random sorting on a table this large causes the queries to take a few seconds, which is not ideal.
One other method I thought of for the non-filtered result set would be to just get PHP to select some random numbers between 1 - 7,000,000 or so and then do an IN(...) with the query to only grab those rows - and yes, I know that this method has a caveat in that you may get less than 4 if a record with that id no longer exists.
However, the above method obviously will not work with the category filtering as PHP doesn't know which record numbers belong to which category and hence cannot select the record numbers to select from.
Are there any better ways I can do this? Only way I can think of would be to store the record id's for each category in another table and then select random results from that and then select only those record ID's from the main table in a secondary query; but I'm sure there is a better way!?
You could of course use the RAND() function on a query using a LIMIT and WHERE (for the category). That however as you pointed out, entails a scan of the database which takes time, especially in your case due to the volume of data.
Your other alternative, again as you pointed out, to store id/category_id in another table might prove a bit faster but again there has to be a LIMIT and WHERE on that table which will also contain the same amount of records as the master table.
A different approach (if applicable) would be to have a table per category and store in that the IDs. If your categories are fixed or do not change that often, then you should be able to use that approach. In that case you will effectively remove the WHERE from the clause and getting a RAND() with a LIMIT on each category table would be faster since each category table will contain a subset of records from your main table.
Some other alternatives would be to use a key/value pair database just for that operation. MongoDb or Google AppEngine can help with that and are really fast.
You could also go towards the approach of a Master/Slave in your MySQL. The slave replicates content in real time but when you need to perform the expensive query you query the slave instead of the master, thus passing the load to a different machine.
Finally you could go with Sphinx which is a lot easier to install and maintain. You can then treat each of those category queries as a document search and let Sphinx randomize the results. This way you offset this expensive operation to a different layer and let MySQL continue with other operations.
Just some issues to consider.
Working off your random number approach
Get the max id in the database.
Create a temp table to store your matches.
Loop n times doing the following
Generate a random number between 1 and maxId
Get the first record with a record Id greater than the random number and insert it into your temp table
Your temp table now contains your random results.
Or you could dynamically generate sql with a union to do the query in one step.
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
Note: my sql may not be valid, as I'm not a mySql guy, but the theory should be sound
First you need to get number of rows ... something like this
select count(1) from tbl where category = ?
then select a random number
$offset = rand(1,$rowsNum);
and select a row with offset
select * FROM tbl LIMIT $offset, 1
in this way you avoid missing ids. The only problem is you need to run second query several times. Union may help in this case.
For MySQl you can use
RAND()
SELECT column FROM table
ORDER BY RAND()
LIMIT 4

Categories