Count the number of occurences of a comma separated string - mysql

I am needing a way to count comma separated values like this - any
suggestions please?
Table:
id (int) | site (varchar)
1 | 1,2,3
2 | 2,3
3 | 1,3
Desired output:
site | # of occurrences
1 | 2
2 | 2
3 | 3

Without getting into exactly what you're doing, I'll assume you have a sites table. If so, it's technically achievable with something like
SELECT sites.site_id AS site, COUNT(1) AS `# of occurrences`
FROM sites
INNER JOIN table ON FIND_IN_SET(sites.site_id, table.site)
GROUP BY sites.site_id
Performance of that will be appalling, as there is no way to use an index, and the data will be able to get inconsistent very easily.
What the comments in your question are alluding to, is to use a relational table of some description, where instead of storing a comma-separated list, you store a row for each 'occurrence'

Related

mySQL - Reiteratively Count rows that have particular CSV string

2-column MySQL Table:
| id| class |
|---|---------|
| 1 | A,B |
| 2 | B,C,D |
| 3 | C,D,A,G |
| 4 | E,F,G |
| 5 | A,F,G |
| 6 | E,F,G,B |
Requirement is to generate a report/output which tells which individual CSV value of class column is in how many rows.
For example, A is present in 3 rows (with id 1,3,5), and C is present in 2 rows (with id 2,3), and G is in 4 rows (3,4,5,6) so the output report should be
A - 3
B - 3
C - 2
...
...
G - 4
Essentially, column id can be ignored.
The draft that I can think of - first all the values of class column need to picked, split on comma, then create a distinct list of each unique value (A,B,C...), and then count how many rows contain the unique value from that distinct list.
While I know basic SQL queries, this is way too complex for me. Am unable to match it with some CSV split function in MySQL. (Am new to SQL so don't know much).
An alternative approach I made it to work - Download class column values in a file, feed it to a perl script which will create a distinct array of A,B,C, then read the downloaded CSV file again foreach element in distinct array and increase the count, and finally publish the report. But this is in perl which will be a separate execution, while the client needs it in SQL report.
Help will be appreciated.
Thanks
You may try split-string-into-rows function to get distinct values and use COUNT function to find number of occurrences. Specifically check here

MySQL/MariaDB find one or more numbers in list, matching lottery numbers with past results

I have a MariaDB table with an archive of past lottery results, imagine EuroMillions or Powerball lotteries.
For example on EuroMillions numbers go from 1 to 50 and then the extra balls from 1 to 12, each result is 5 numbers form the main pool and 2 from the extra pool. So my historic results table could look like this:
Lottery Results table
(other columns like id, date, draw number, etc) | main_numbers | extra_numbers | (timestamp columns)
... | 1,2,3,4,5 | 1,2 | ...
... | 3,12,34,35,45 | 5,11 | ...
... | 4,15,34,39,45 | 10,11 | ...
... | 7,11,25,28,44 | 10,12 | ...
(you get the idea, I have thousands of records...)
So I could select main_numbers and get result "3,12,34,35,45" for that second example row. And for the extra_numbers I would get "5,11".
What I want is to given a set of numbers for main and extra to see if they match any of my results, finding any number of numbers (numbered lottery balls).
So for example if I SELECT to find main_numbers "5,9,22,34,45" with extra_numbers "2,11" I would get (from my extracted example) two records:
... | 3,12,34,35,45 | 5,11 | ...
... | 4,15,34,39,45 | 10,11 | ...
Matching two main numbers and one extra number, in this case finding lottery prizes in the results table. Makes sense?
I'm using MariaDB and I'm a bit lost on how to proceed, I tried WHERE IN, FIELD_IN_SET, etc.
Is there a way to perform a SELECT to find results in only one statement or do I have to pick all records and then iterate elsewhere, php for example?
My aim would be to have it in one statement, so I could just send the numbers and get the matching records... Possible?
I hope this makes sense.
Many thanks for your answers.
Consider the following.
For simplicity, let's say that a lottery comprises 3 main balls, and two bonus balls:
DROP TABLE IF EXISTS lottery_results;
CREATE TABLE lottery_results
(draw_id INT NOT NULL
,ball_no INT NOT NULL
,ball_val INT NOT NULL
,PRIMARY KEY(draw_id,ball_no)
);
INSERT INTO lottery_results VALUES
(1,1,22),
(1,2,35),
(1,3,62),
(1,4,27),
(1,5,17),
(2,1,18),
(2,2,33),
(2,3,49),
(2,4, 4),
(2,5,35);
And we want to find all results where 34, 35, or 36 were drawn as a main number...
SELECT draw_id
FROM lottery_results
WHERE ball_no <=3
AND ball_val IN(34,35,36);
+---------+
| draw_id |
+---------+
| 1 |
+---------+
Thanks Strawberry,
I found a solution if I have all numbers in distinct columns, but could I find if they are in the same column in CSV?
So if I put my CSV in distinct columns for numbers (n_1...n_5) and extra numbers for the stars in (s_1, s_2) I can seek matched in those multiple columns.
This is using multiple columns:
To find matches numbers 1,2,3,4,5 with stars 1,2...
In EuroMillions you get a prize with 2 or more numbers and any star (one or two).
SELECT
main_numbers, extra_numbers,
((n_1 IN (1,2,3,4,5)) +
(n_2 IN (1,2,3,4,5)) +
(n_3 IN (1,2,3,4,5)) +
(n_4 IN (1,2,3,4,5)) +
(n_5 IN (1,2,3,4,5))) AS matched_numbers,
((s_1 IN (1,2)) +
(s_2 IN (1,2))) AS matched_stars,
created_at
FROM `lottery_results_archive`
HAVING matched_numbers >= 3 OR matched_numbers = 2 AND matched_stars > 0
ORDER BY matched_numbers DESC, matched_stars DESC, created_at DESC
Makes sense?
Thanks.

mySQL Multi Join from 2 Statements

I've found many similar questions but have not been able to understand / apply the answers; and I don't really know what to search for...
I have 2 tables (docs and words) which have a many to many relationship. I am trying to generate a list of the top 5 most frequently used words that DO NOT appear in a specified docs.
To this end I have 2 mySQL queries, each of which takes me part way to achieving my goal:
Query #1 - returns words sorted by frequency of use, falls short because it also returns ALL words (SQLFiddle.com)
SELECT `words_idwords` as wdID, COUNT(*) as freq
FROM docs_has_words
GROUP BY `words_idwords`
ORDER BY freq DESC, wdID ASC
Query #2 - returns words that are missing from specified document, falls short because it does not sort by frequency of use (SQLFiddle.com)
SELECT wordscol as wrd, idwords as wID
FROM `words` where NOT `idwords`
IN (SELECT `words_idwords` FROM `docs_has_words` WHERE `docs_iddocs` = 1)
But what I want the output to look like is:
idwords | wordscol | freq
-------------------------
| 8 | Dog | 3 |
| 3 | Ape | 2 |
| 4 | Bear | 1 |
| 6 | Cat | 1 |
| 7 | Cheetah | 1 |
| 5 | Beaver | 0 |
Note: `Dolphin`, one of the most frequently used words, is NOT in the
list because it is already in the document iddocs = 1
Note: `Beaver`, is a "never used word" BUT is in the list because it is
in the main word list
And the question is: how can I combine these to queries, or otherwise, get my desired output?
Basic requirements:
- 3 column output
- results sorted by frequency of use, even if use is zero
Updates:
In light of some comments, the approach that I was thinking of when I came up with the 2 queries was:
Step 1 - find all the words that are in the main word list but not used in document 1
Step 2 - rank words from Step 1 according to how many documents use them
Once I had the 2 queries I thought it would be easy to combine them with a where clause, but I just can't get it working.
A hack solution could be based on adding a dummy document that contains all the words and then subtract 1 from freq (but I'm not that much of a hack!).
I see now what the problem is. I was mislead by your statement regarding the results of the 1st query (emphasis is mine):
returns words sorted by frequency of use, falls short because it also returns ALL words
This query does not return all words, it only returns all used words.
So, you need to left join the words table on docs_has_words table to get all words and eliminate the words that are associated with doc 1:
SELECT w.idwords as wdID, w.wordscol, COUNT(d.words_idwords) as freq
FROM words w
LEFT JOIN `docs_has_words` d on w.idwords=d.words_idwords
WHERE w.idwords not in (SELECT `words_idwords` FROM `docs_has_words` WHERE `docs_iddocs` = 1)
GROUP BY w.idwords
ORDER BY freq DESC, wdID ASC;
See sqlfiddle
I think #Shadow has it right in his comment, you just need to add the where clause like this: sqlFiddle
SELECT
`words_idwords` as wdID,
COUNT(*) as freq
FROM docs_has_words
WHERE NOT `words_idwords` IN (SELECT `words_idwords` FROM `docs_has_words` WHERE `docs_iddocs` = 1)
GROUP BY `words_idwords`
ORDER BY freq DESC, wdID ASC
Does this produce the output you need?

Combining multiple select queries in to one table

I've been racking my brains for a while and I'm sure there is a simple solution to it, but for the life of me it's not obvious.
I want to query a database in MySQL Workbench to return a set of serial numbers for a given part number, which is of a fairly basic form:
Select serial_num as sn12345
from process
where part_number = 12345
Thus my output is
sn12345
---------------
0000001
0000002
etc
Now I have a number of part numbers I want to get the serial numbers of so that my output is like
sn12345 | sn12346 | sn12347 |
------------------------------------------
0000001 | 0000005 | 0000008 |
0000002 | 0000006 | 0000009 |
Assume that there are more columns than just these. However, I do not want to UNION the query as I want output in individual columns. Also, there may be different numbers of serial number entries for each part number, i.e 100 for one, but 1000 for a second, and 5 for a third, etc, so I'll probably have a lot of NULL entries.
Thanks in advance!

MySQL query get column value similar to given

Sorry if my question seems unclear, I'll try to explain.
I have a column in a row, for example /1/3/5/8/42/239/, let's say I would like to find a similar one where there is as many corresponding "ids" as possible.
Example:
| My Column |
#1 | /1/3/7/2/4/ |
#2 | /1/5/7/2/4/ |
#3 | /1/3/6/8/4/ |
Now, by running the query on #1 I would like to get row #2 as it's the most similar. Is there any way to do it or it's just my fantasy? Thanks for your time.
EDIT:
As suggested I'm expanding my question. This column represents favourite artist of an user from a music site. I'm searching them like thisMyColumn LIKE '%/ID/%' and remove by replacing /ID/ with /
Since you did not provice really much info about your data I have to fill the gaps with my guesses.
So you have a users table
users table
-----------
id
name
other_stuff
And you like to store which artists are favorites of a user. So you must have an artists table
artists table
-------------
id
name
other_stuff
And to relate you can add another table called favorites
favorites table
---------------
user_id
artist_id
In that table you add a record for every artist that a user likes.
Example data
users
id | name
1 | tom
2 | john
artists
id | name
1 | michael jackson
2 | madonna
3 | deep purple
favorites
user_id | artist_id
1 | 1
1 | 3
2 | 2
To select the favorites of user tom for instance you can do
select a.name
from artists a
join favorites f on f.artist_id = a.id
join users u on f.user_id = u.id
where u.name = 'tom'
And if you add proper indexing to your table then this is really fast!
Problem is you're storing this in a really, really awkward way.
I'm guessing you have to deal with an arbitrary number of values. You have two options:
Store the multiple ID's in a blob object in JSON format. While MySQL doesn't have JSON functions built in, there are user defined functions that will extract values for you, etc.
See: http://blog.ulf-wendel.de/2013/mysql-5-7-sql-functions-for-json-udf/
Alternatively, switch to PostGres
Add as many columns to your table as the maximum number of ID's you expect to have. So if /1/3/7/2/4/8/ is the longest entry, have 6 columns in your table. Reason this is bad: you'll have sparse columns that'll unnecessarily slow your tables.
I'm sure you could write some horrific regex to accomplish the task, but I caution on using complex regex's on enormous tables.