SQLite Find missing numbers between range (3000-4000) - php

I'm looking to find the lowest missing number between 2 numbers.
there may be jumps between 3080 and 3085, etc and all I would need is 3081.
This is for assigning an id to an entry and these entries can be deleted thus the id is too and because we have a limited range we want to make sure we use all the numbers. So if there aren't any missing numbers it would have to grab the next available number. if it's full till 3850 then it would need to grab 3851.
They are 0 if they have been deleted rather than null if that helps.
SELECT number, user_id
FROM entries
WHERE user_id = '18'
AND number BETWEEN '3000' AND '4000'
I'm not sure how to get the lowest available number in this sequence, please help. Thank you.

Try using a self join:
SELECT MIN(e1.number) + 1 num_missing
FROM entries e1
LEFT JOIN entries e2
ON e2.number = e1.number + 1
WHERE e1.id = 18 AND e1.number BETWEEN 3000 AND 4000 AND
e2.number IS NULL;
The critical condition in the WHERE clause is e2.number IS NULL, which means that this particular number had no next immediate value in the sequence, i.e. it is the start of a gap.

If you want next missing number for an id, then:
select e.id, e.number + 1
from entries e
where not exists (select 1
from entries e2
where e2.id = e.id and e2.number = e.number + 1
);
If you want this for a particular id, then add id = 18 to the outer where clause.

Related

Mysql UNION ALL to count multiple columns BUT also insert date column

Hi Guys I have a question. I am still learning and am trying to get some date out. Beneath is the table. It has hundreds of lines, but for example:
FormNR
Datum
XX1
XX2
XX3
0001
2022-09-08
4
23
7
0002
2022-09-10
8
5
0
The table name is 'forms'. Now what I need to do is to count XX1+XX2+XX3 (for a year rapport). Then I have a 'date from and to' selection box on my page. So the question would be:
What instanties have been used between a certain date in total but so that you can see a a total per Instantie (each number is a different instantie).
So for example...Between the 1st of January and the 1st of June a list of all XX numbers ( there are 36 ) with there total behind it
What I have is the following. Is works great and shows all XX's in a nice table but for the entire table, not per date. As soon as i want to add the 'between $date_from AND $date_to' it fails.
<?php
$sql_rg_total="SELECT forms.Datum, x.f1,Count(x.f1)
FROM
(SELECT XX1 As F1 FROM forms
UNION ALL
SELECT XX2 As F1 FROM forms
UNION ALL
SELECT XX3 As F1 FROM forms) x
WHERE x.f1 = '$subcat_id'
GROUP BY x.f1";
$resultvv=mysqli_query($conn, $sql_rg_total);
if (mysqli_num_rows($resultvv) > 0) {
while ($rowvv = mysqli_fetch_assoc($resultvv)) {
$subnr = $rowvv['Count(x.f1)'];
echo $subnr;
}
}
?>
By the way $subcat_id is from another table which connects the number to a name.
I have tried to write it as clear as I could. I know it's a bit thought haha. Thanks anyway for any input. Really stuck.
This query should do it:
SELECT SUM(x.c) AS c
FROM (
SELECT ((XX1 = '$subcat_id') + (XX2 = '$subcat_id') + (XX3 = '$subcat_id')) AS c
FROM forms
WHERE Datum BETWEEN '$date_from' AND '$date_to'
) x
The value of a boolean condition is 1 when it's true, 0 when it's false. So XX1 = '$subcat_id' + XX2 = '$subcat_id' + XX3 = '$subcat_id' adds up the number of columns that match in a row, then SUM(c) totals them in the entire table.
You don't need GROUP BY, since it's the same column that you're filtering in the WHERE condition (and now in the SELECT expression). And this moves the date condition into the subquery.

How to edit this mySQL so as to combine the rows of the same id into 1 instead of 3?

The following mySQL query gets data from 2 tables, alerts_data and alerts_list. The first table has the data of an alert, and the second has the description of the alert. So in the alerts_data there are multiple rows with the same alerts_data_id that is the same with the alerts_id of alerts_list.
What i want to achieve, is to display something like this
alert number 51, 5 clicked , 2 closed
alert number 57, 13 clicked, 3 closed, 8 waiting
using mySQL or PHP (i do not know if i can get this through plain mySQL)
So for now with my knowledge I can not display the data of alert 51 in one row, but because of the different alerts_data_status i have to show 3 rows for each.
How can I do it as above?
SELECT COUNT( alerts_data_id ) AS total, alerts_data_id, alerts_data_status, alerts_list.alerts_title
FROM alerts_data
JOIN alerts_list ON
alerts_data.alerts_data_id = alerts_list.alerts_id
GROUP BY alerts_data_id, alerts_data_status
//output
total - alerts_data_id - alerts_data_status - alerts_title
5 - 51 - clicked - alert number 51
2 - 52 - closed - alert number 51
13 - 57 - clicked - alert number 57
3 - 57 - waiting - alert number 57
8 - 57 waiting - alert number 57
Note: the alerts number are just examples, it can be any number
// alert_data
id - alerts_data_id - alerts_data_status
// alerts_list
alerts_id - alerts_name - alerts_text
Here's a sqlfiddle: http://sqlfiddle.com/#!9/c70c2/1
This may be an application for GROUP_CONCAT().
You first want a summary of your alerts by alerts_data_id and alerts_data_status. This is a little complex, because your sqlfiddle has a whole bunch of empty alerts_data_status strings. Here, I'm replacing those empty strings with `?'. (http://sqlfiddle.com/#!9/c70c2/23/0)
SELECT COUNT(*) AS alerts_count,
alerts_data_id,
CASE WHEN LENGTH(alerts_data_status) = 0 THEN '?'
ELSE alerts_data_status END AS alerts_data_status
FROM alerts_data
GROUP BY alerts_data_id, alerts_data_status
You then want to roll that up inside another query
SELECT SUM(a.alerts_count) total,
a.alerts_data_id, b. alerts_name,
GROUP_CONCAT( CONCAT(a.alerts_count, ': ', a.alerts_data_status)
ORDER BY a.alerts_data_status
SEPARATOR "; " ) detail
FROM (
SELECT COUNT(*) AS alerts_count,
alerts_data_id,
CASE WHEN LENGTH(alerts_data_status) = 0 THEN '?'
ELSE alerts_data_status END AS alerts_data_status
FROM alerts_data
GROUP BY alerts_data_id, alerts_data_status
) a
JOIN alerts_list b ON a.alerts_data_id = b.alerts_id
GROUP BY a.alerts_data_id, b.alerts_name
This will give you one row for each distinct alerts_data_id. Each alert is identified by its count, its id, and its name. (http://sqlfiddle.com/#!9/c70c2/26/0)
Then the row will contain a semicolon-separated list of the counts of the different alert status.
If i understand you well i think here is what you need;
SELECT
COUNT( alerts_data.id ) AS total,
ad.id,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='clicked') as clicked,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='closed') as closed,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='waiting') as waiting,
FROM alerts_data ad
GROUP BY ad.id
Was checking other answers and your SQLFIDDLE and thought this might be a nicer approach:
SELECT alerts_list.alerts_title,
SUM(CASE WHEN alerts_data_status = 'clicked' THEN 1 ELSE 0 END) AS clicked,
SUM(CASE WHEN alerts_data_status = 'closed' THEN 1 ELSE 0 END) AS closed,
SUM(CASE WHEN alerts_data_status = 'waiting' THEN 1 ELSE 0 END) AS waiting
FROM alerts_data
JOIN alerts_list ON alerts_data.alerts_data_id = alerts_list.alerts_id
GROUP BY alerts_list.alerts_title

mySQL search for id in array

I want to filter my SQL query with some array of IDs.
so I have IDs for example: 2,3,4 and activity_meta.value 1,2,5;
And I want to find every activity where it has id in activity_meta.value
SELECT DISTINCT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name
FROM wp_bp_activity a
LEFT JOIN wp_users u
ON a.user_id = u.ID
INNER JOIN wp_bp_activity_meta
ON (a.id = wp_bp_activity_meta.activity_id)
WHERE a.is_spam = 0
AND a.hide_sitewide = 0
AND a.type != 'activity_comment'
AND (wp_bp_activity_meta.meta_key = 'activity_tagz' )
DESC LIMIT 0, 20
I want to add AND (1,2,3 IN wp_bp_activity_meta.meta_value)
I just dont know how to treat serialized array;
The value of activity_meta.value is not normalized. It would be a better choice, to create a second table, where you assign the meta tags to your element.
So, it would look like:
wp_bp_activity_id | meta
1 | 1
1 | 2
1 | 3
let's cal this table "meta_relation". Then you simple could either use a subselect, like this:
... WHERE wp_bp_activity.id in (SELECT wp_bp_activity_id FROM meta_relation WHERE meta in (1,2,5))
Or you could implement this with another join, like
... INNER JOIN meta_relation ON wp_bp_activitiy.id = meta_relation.wp_bp_activity_id
(will return 3 rows then for 3 matching tags)
... WHERE meta in (1,2,5)
... GROUP BY wp_bp_activity.id
(will remove any duplicate result and unwanted tags)
For your current scenario you could use a workaround. However this requires to build up the query programmatically:
For each "Tag", you want to find (i.e. 1,2,5) you need to add another or-condition.
To make sure you are not matching the 2 within 125 you can sourround it by ,.
To make sure, you are not missing the FIRST or LAST item (which has no leading / trailing , you need to concatenate the column with 2 more , in the first place:
Query then contains the following additional criterias.
SELECT
....
WHERE
...
AND
(
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,1,%" OR
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,2,%" OR
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,5,%"
)
if you want all 3 tags to appear, use AND instead of OR.
in your example, this will match :
,2,3,4, against ,1,
,2,3,4, against ,2, //match
,2,3,4, against ,5,
Order and / or gaps don't matter with this approach.
(Depending on whether its a 10 User Website or a Million-Customer-Portal, you can use either. Preferred Solution is to normalize your table.)

algorithm/query to get statistical data from table using php/mysql

I have the following table structure:
CREATE TABLE test(
myID INT,
num1 INT,
num2 INT,
num3 INT,
PRIMARY KEY (myID)
)engine=innodb;
now i have the following data in the table:
myID num1 num2 num3
1 15 27 98
2 27 38 66
3 15 27 77
now i need to run 2 queries, first query runs to select all numbers and on PHP side i count the number of times each number appeared (frequencies), the second query should select the second most frequent number.
First query:
$numfreq = PDO->prepare('
SELECT num1, num2, num3
FROM test
');
$numfreq->execute();
$allNums = array();
while ($row = $numfreq->fetch(PDO::FETCH_ASSOC)) {
$allNums[] = intval($row['num1']);
$allNums[] = intval($row['num2']);
$allNums[] = intval($row['num3']);
}
$numFrequencies = array_count_values($allNums);
arsort($numFrequencies);
this correctly returns frequencies of each number from the table. Now for the second part
THIS IS WHERE I NEED HELP:
I get the most frequent appeared number in this case its 27 since its frequency is 3, I need to select that 1 number that appears the most next to 27 means i need to get somehow number 15 since it appears twice next to 27.
i can probably figure out algorithm on PHP side but i was wondering if its possible to do it using query?
so the final result would be:
most frequent number: 27
most frequent number 27 combined with 15 appears 2 times and is most frequent combination.
select val, count(val) as frequency
from
(select num1 as val from test
union all
select num2 as val from test
union all
select num3 as val from test
) as b
group by val
order by frequency desc
limit 2
Sqlfiddle here.
The inner query converts the three columns into a result set with just one column - highlight the inner query, and you'll see how it works. We then use that result set as the source for the counting/ordering query.

MYSQL : How to set to same position if row value is the same?

position | Average | gpmp
1 70.60 2.0
2 60.20 2.3
3 59.80 4.8
4 59.80 4.8
5 45.70 5.6
Hie All,
As above table, I need to arrange the position according to the lowest gpmp and the highest average. But when the both average and gmp are the same, I will need to have the position to be the same.
For example, position 3 and 4 have the same average and gpmp. How do I generate the mysql query or using php function so that after they detect the same average and gpmp and change the position 4 to 3.
Which mean after the function is generated it will become like the table below.
position | Average | gpmp
1 70.60 2.0
2 60.20 2.3
3 59.80 4.8
3 59.80 4.8
5 45.70 5.6
Here's a simple way to update the table as you described in your post - taking the sequential positions and updating them accordingly. It doesn't calculate the positions or anything, just uses the data already there:
UPDATE `table` t SET position = (
SELECT MIN(position) FROM (SELECT * FROM `table`) t2 WHERE t.Average = t2.Average AND t.gpmp = t2.gpmp
)
I'd give something like the following a try, through it does assume a primary key is on this table. Without a primary key you're going to have issues updating specific rows easily / you'll have a lot of duplicates.
So for this example I'll assume the table is as follows
someTable (
pkID (Primary Key),
position,
Average,
gpm
)
So the following INSERT would do the job I expect
INSERT INTO someTable (
pkID,
position
)
SELECT
someTable.pkID,
calcTable.position
FROM someTable
INNER JOIN (
SELECT
MIN(c.position) AS position,
c.Average,
c.gpm
FROM (
// Calculate the position for each Average/gpm combination
SELECT
#p = #p + 1 AS position,
someTable.Average,
someTable.gpm
FROM (
SELECT #p:=0
) v,someTable
ORDER BY
someTable.Average DESC,
someTable.gpmp ASC
) c
// Now regroup to get 1 position for each combination (the lowest position)
GROUP BY c.Average,c.gpm
) AS calcTable
// And then join this calculated table back onto the original
ON (calcTable.Average,calcTable.gpm) = (someTable.Average,someTable.gpm)
// And rely on the PK IDs clashing to allow update
ON DUPLICATE KEY UPDATE position = VALUES(position)
(pseudo code)
select * from table
get output into php var
foreach (php row of data)
is row equal to previous row?
yes - don't increment row counter, increment duplicate counter
no - increment row counter with # of duplicates and reset duplicate counter
save current row as 'previous row'
next
you can try something like this in php:
$d= mysql_query('select distinct gpmp from tablename order by gpmp');
pos= 1;
while($r= mysql_fetch_array($d)){
mysql_query('update tablename set position='.$pos.' where gpmp='.$r['gpmp']);
$pos++;
}
You only need to "expand" the idea to take averange in account too.

Categories