I have a table with a column, lets call it "query", which is a varchar.
I would like to retrieve the values into a paginated list in such a way that each page will contain 208 results, 8 from each letter in the alphabet.
So on page 1 the first 8 results will begin with "a", the next 8 will begin with "b", and so on until "z" (if there aren't any results for that letter then it just continues to the next letter.
On page 2 of the results it would show the next 8 results beginning with "a", the next with "b", and so on.
Basically instead of sorting by query ASC, which will result in the first page having all words beginning with "a", I would like each page to contain words beginning with each letter of the alphabet.
If you feel I did not explain myself properly (I do!), then please feel free to ask. I have the idea in my head but it's not easy translating it into words!
a naive start approach could be:
SELECT * FROM table1 WHERE `query` LIKE 'a%' ORDER BY `query` LIMIT 8
UNION
SELECT * FROM table1 WHERE `query` LIKE 'b%' ORDER BY `query` LIMIT 8
UNION
SELECT * FROM table1 WHERE `query` LIKE 'c%' ORDER BY `query` LIMIT 8
....
The second page would need to be done using
SELECT * FROM table1 WHERE `query` LIKE 'a%' ORDER BY `query` LIMIT 8,8
UNION
....
Third page:
SELECT * FROM table1 WHERE `query` LIKE 'a%' ORDER BY `query` LIMIT 16,8
UNION
....
etc.
I think this is not possible to do in one statement (or, may be possible, but too slow in execution). May be you must take a look on MySQL prepared statements or change behavior of the page.
you can use group by the first letter using
group by left(query, 1)
And from the server side script, show 8 results from the query result (perhaps by using the page page number * 8 and starting from that index for each letter, starting from page 0, that is)
I would never use this query on large dataset but just for personal fun I found this solution:
select *,
ceil(row_num/8) as gr
from (
select
*,
#num := if(#word = substring(word,1,1), #num + 1, 1) as row_num,
#word := substring(word,1,1) as w
from words,(select #num:=0,#word:='') as r order by word ) as t
order by gr,word
Related
From a mySQL table I would like to determine the most frequent starting letter; for example if the list is:
day
book
cat
dog
apple
The expected result would ultimately allow me to determine that:
'd' is the most frequent starting letter
'd' has a count of 2
Is there a way to do this without running 26 queries, e.g.:
WHERE myWord LIKE 'a%'
WHERE myWord LIKE 'b%'
...
WHERE myWord LIKE 'y%'
WHERE myWord LIKE 'z%'
I found this SO question which makes me think I can do this in 2 steps:
If I'm not mistaken the approach would be to first build a list of all the first letters using the approach from this SO Answer something like this:
SELECT DISTINCT LEFT(word_name, 1) as letter, word_name
FROM word
GROUP BY (letter)
ORDER BY letter
which I expect would look something like:
a
b
c
d
d
... and then query that list. To do this I would store that new list as a temporary table as per this SO question, something like:
CREATE TEMPORARY TABLE IF NOT EXISTS table2 AS (SELECT * FROM table1)
and query that for Magnitude as per this SO question, something like.
SELECT column, COUNT(*) AS magnitude
FROM table
GROUP BY column
ORDER BY magnitude DESC
LIMIT 1
Is this a sensible approach?
NOTE:
As sometimes happens, in writing this question I think I figured out a way forward, as yet I have no working code. I'll update the question later with code that either works or which needs help.
In the meanwhile I appreciate any feedback, pointers, proposed answers.
Finally, I'm using PHP, PDO, mySQL for this.
TIA
For what it's worth there was an easier way, this is what I ended up with thanks to both who took the time to answer:
$stmt_common2 = $pdo->prepare('SELECT COUNT(*) as occurence,SUBSTRING(word,1,1) as letter
FROM words
GROUP BY SUBSTRING(word,1,1)
ORDER BY occurence DESC, letter ASC
LIMIT 1');
$stmt_common2->execute();
$mostCommon2 = $stmt_common2->fetchAll();
echo "most common letter: " . $mostCommon2[0]['letter'] . " occurs " . $mostCommon2[0]['occurence'] . " times)<br>";
You can achieve by using this simple query
SELECT COUNT(*) as occurence,SUBSTRING(word_name,1,1) as letter
FROM word
GROUP BY SUBSTRING(word_name,1,1)
ORDER BY occurence DESC, letter ASC
LIMIT 1
I have a table like this:
id path
1 /
2 /city/
3 /city/europe/
4 /city/north america/
6 /city/europe/germany/berlin/
7 /city/europe/germany/
8 /city/north america/usa/
[...]
and a search string like this:
"/city/north america/usa/florida/miami/"
I need to select the longest path thats inside the search string so the result would be
id path
8 /city/north america/usa/
Every parent to any path in the table exists. The paths can be arbitrarily long. The search string contains at least "/".
I can think of a few solutions like using wildcards on the column, splitting the search string and performing a select with IN or joins.
What is the best and fastest way to do this?
The table can be modified in any way.
EDIT:
The fastest solution with which I have come up so far:
SELECT * FROM table_name
WHERE path IN ('/','/city/','/city/north america/', '/city/north america/usa/', '/city/north america/usa/florida/', '/city/north america/usa/florida/miami/')
ORDER by LENGTH(path) DESC LIMIT 1;
SELECT *
FROM table_name
WHERE path REGEXP '^/city((((/north america)?/usa)?/florida)?/miami)?\/?$'
ORDER BY LENGTH(path) DESC
LIMIT 1
Or, try this one:
select * FROM tbl WHERE path=
( select max(path) from tbl
where '/city/north america/usa/florida/miami/' like concat(path,'%')
)
(Edit)
Then maybe this is faster:
select * from tbl where instr('/city/north america/usa/florida/miami/',path)=1
order by path desc limit 1
I have one table named dictionarydefinition.
CREATE TABLE dictionarydefinition (
id bigint NOT NULL,
definition character varying(1024) NOT NULL,
word character varying(200) NOT NULL,
grammertypename character varying(20) NOT NULL,
)
I have sql command Select * from dictionarydefinition where word like 'someword%'.
Results are multiple rows that got same value. For example if someword% is just empty ''
the result will be:
A
A
A
B
B
C
D
D
D
I just want result be:
A
B
C
D
I have used GROUP BY command, but it takes too much time to process 30MB database for my android device.
What kind of SQL commands I can add to make it choose only one row which got someword% value?
For the example you have mentioned, you can use GROUP BY.. Suppose the column-name for the alphabets is word, the command would be :
SELECT * from dictionarydefinition where word like 'someword%' GROUP BY word;
You can use SELECT TOP or LIMIT or ROWNUM
SELECT TOP 1 * from dictionarydefinition where word like 'someword%';
or
SELECT * from dictionarydefinition where word like 'someword%' LIMIT 1;
or
SELECT * from dictionarydefinition where word like 'someword%' AND ROWNUM <= 3
I've the following query. It selects all posts where the title contains the words green, blue or red.
SELECT id, title FROM post WHERE title REGEXP '(green|blue|red)'
I would like to sort the results in such a way that the title with the most matches (all three words) and thus the most relevant one, is listed first. Is this possible in this scenario and if so, how I would go on about it?
Thanks
You must split the regex. Either to different conditions or different queries:
SELECT COUNT(results.username) as count, results.* FROM (
SELECT * FROM `post` WHERE `title` LIKE "%blue%"
UNION SELECT * FROM `post` WHERE `title` LIKE "%red%"
UNION SELECT * FROM `post` WHERE `title` LIKE "%green%"
) as results GROUP BY results.title ORDER BY count DESC;
Note: I used LIKE instead of REGEXP, becouse when you split the condition you wont need it anymore according to your example. LIKE is a bit faster then regex, but if your pattern is more complex, then you can always replace it back.
I have a MySQL table named "content"containing (a.o.) the fields "_date" and "text", for example:
_date text
---------------------------------------------------------
2011-02-18 I'm afraid my car won't start tomorrow
2011-02-18 I hope I'm going to pass my exams
2011-02-18 Exams coming up - I'm not afraid :P
2011-02-19 Not a single f was given this day
2011-02-20 I still hope I passed, but I'm afraid I didn't
2011-02-20 On my way to school :)
I'm looking for a query to count the number of times the words "hope" and "afraid" are being used per day. In other words, the output would have to be something like:
_date word count
-----------------------
2011-02-18 hope 1
2011-02-18 afraid 2
2011-02-19 hope 0
2011-02-19 afraid 0
2011-02-20 hope 1
2011-02-20 afraid 1
Is there an easy way to do this or should I just write I different query per term? I now have this, but I don't know what to put instead of "?"
SELECT COUNT(?) FROM content WHERE text LIKE '%hope' GROUP BY _date
Can somebody help met with the correct query for this?
I think the most easy and redable way is to make subquerys:
Select
_date, 'hope' as word,
sum( case when `text` like '%hope%' then 1 else 0 end) as n
from content
group by _date
UNION
Select
_date, 'afraid' as word,
sum( case when `text` like '%afraid%' then 1 else 0 end) as n
from content
group by _date
This approach has not the best performace. If you are looking for performance you should grouping in subquery by day, also this like condition is a performance killer. This is a solution if you only execute the query in batch mode time by time. Explain your performance requeriments for an accurate solution.
EDITED TO MATCH LAST OP REQUERIMENT
Your query is almost correct:
SELECT _date, 'hope' AS word, COUNT(*) as count
FROM content WHERE text LIKE '%hope%' GROUP BY _date
use %hope% to match the word anywhere (not only at the end of the string). COUNT(*) should do what you want.
To get multiple words from a single query, use UNION ALL
Another approach is to create a sequence of words on the fly and use it as the second table in a join:
SELECT _date, words.word, COUNT(*) as count
FROM (
SELECT 'hope' AS word
UNION
SELECT 'afraid' AS word
) AS words
CROSS JOIN content
WHERE text LIKE CONCAT('%', words.word, '%')
GROUP BY _date, words.word
Note that it will only count a single occurrence of each word per sentence. So »I hope there is still hope« will only give you 1, and not 2
To get 0 when there are no matches, join the previous result with the dates again:
SELECT content._date, COALESCE(result.word, 'no match'), COALESCE(result.count, 0)
FROM content
LEFT JOIN (
SELECT _date, words.word, COUNT(*) as count
FROM (
SELECT 'hope' AS word
UNION
SELECT 'afraid' AS word
) AS words
CROSS JOIN content
WHERE text LIKE CONCAT('%', words.word, '%')
GROUP BY _date, words.word ) AS result
ON content._date = result._date
Assuming you want to count all words and find the most used words (rather than looking for the count of a few specific words) you might want to try something like the following stored procedure (string splitting compliments of this blog post):
DROP PROCEDURE IF EXISTS wordsUsed;
DELIMITER //
CREATE PROCEDURE wordsUsed ()
BEGIN
DROP TEMPORARY TABLE IF EXISTS wordTmp;
CREATE TEMPORARY TABLE wordTmp (word VARCHAR(255));
SET #wordCt = 0;
SET #tokenCt = 1;
contentLoop: LOOP
SET #stmt = 'INSERT INTO wordTmp SELECT REPLACE(SUBSTRING(SUBSTRING_INDEX(`text`, " ", ?),
LENGTH(SUBSTRING_INDEX(`text`, " ", ? -1)) + 1),
" ", "") word
FROM content
WHERE LENGTH(SUBSTRING_INDEX(`text`, " ", ?)) != LENGTH(`text`)';
PREPARE cmd FROM #stmt;
EXECUTE cmd USING #tokenCt, #tokenCt, #tokenCt;
SELECT ROW_COUNT() INTO #wordCt;
DEALLOCATE PREPARE cmd;
IF (#wordCt = 0) THEN
LEAVE contentLoop;
ELSE
SET #tokenCt = #tokenCt + 1;
END IF;
END LOOP;
SELECT word, count(*) usageCount FROM wordTmp GROUP BY word ORDER BY usageCount DESC;
END //
DELIMITER ;
CALL wordsUsed();
You might want to write another query (or procedure) or add some nested "REPLACE" statements to further remove punctuation from the resulting temp table of words, but this should be a good start.