简体   繁体   English

PHP for循环使用MySQL行,具体取决于MySQL查询结果

[英]PHP for-loop with MySQL rows depending on the MySQL query result

I can't figure out if there's a way to do something like this: 我无法弄清楚是否有办法做这样的事情:

1 - Get user input (any signed/positive integer) 1 - 获取用户输入(任何有符号/正整数)

2 - Iterate through each number (eg user input a positive integer like 10, so loop goes through 10 iterations) 2 - 遍历每个数字(例如,用户输入一个正整数,如10,所以循环经历10次迭代)

3 - PHP code loops through MySQL table in Database. 3 - PHP代码循环通过数据库中的MySQL表。 MySQL table looks like this: MySQL表看起来像这样:

+----+------+------+---------+
| ID | ITEM | USER | ACTIONS |
+----+------+------+---------+
|  1 | 1234 |    5 |       0 |
|  2 | 1234 |    3 |       0 |
|  3 | 1234 |    1 |       0 |
|  4 | 1234 |    7 |       0 |
+----+------+------+---------+

4 - PHP code should loop through each of those 4 ACTIONS rows to find the lowest number, and start updating the ACTIONS field until there is a total number of 10 'actions' in the table. 4 - PHP代码应遍历这4个ACTIONS行中的每一行以找到最小的数字,并开始更新ACTIONS字段,直到表中总共有10个'actions'为止。 So after the user had input a value of 10, this is how the above table would look, if we start from the lowest number: 因此,在用户输入值10之后,如果我们从最小数字开始,这就是上表的外观:

+----+------+------+---------+
| ID | ITEM | USER | ACTIONS |
+----+------+------+---------+
|  1 | 1234 |    5 |       3 |
|  2 | 1234 |    3 |       3 |
|  3 | 1234 |    1 |       2 |
|  4 | 1234 |    7 |       2 |
+----+------+------+---------+

5 - If another user inputs 10, this is how the table would look: (note that the starting point for this user would be the 3rd row ie the integer 2, since its the first occurrence of the smallest number): 5 - 如果另一个用户输入10,这就是表格的外观:(注意该用户的起点是第3行,即整数2,因为它是第一次出现的最小数字):

+----+------+------+---------+
| ID | ITEM | USER | ACTIONS |
+----+------+------+---------+
|  1 | 1234 |    5 |       5 |
|  2 | 1234 |    3 |       5 |
|  3 | 1234 |    1 |       5 |
|  4 | 1234 |    7 |       5 |
+----+------+------+---------+

Please advise how this can be done, I know sufficient PHP and MySQL code so you can suggest advanced syntax, but I can't figure how it should be done. 请告知如何做到这一点,我知道足够的PHP和MySQL代码,所以你可以建议高级语法,但我无法弄清楚它应该如何完成。 I don't know where to start. 我不知道从哪里开始。 Besides iterating through the MySQL rows and using a for loop to iterate through each number the user input, how should I go about getting the expected result? 除了遍历MySQL行并使用for循环迭代用户输入的每个数字外,我该如何获得预期的结果呢?

If concurrency or table locks would not be a problem, you could achieve with two query, a SELECT and then a single UPDATE, so you could calculate the final result and modify the value just one time per row. 如果并发或表锁不是问题,您可以使用两个查询,一个SELECT然后一个UPDATE来实现,这样您就可以计算最终结果并且每行只修改一次值。

Increasing the numerosity of records, performance may start to become a problem, so it would worth limiting the SELECT to the first n (which is the positive integer) records, and to do so, be sure to have added an index to the primary incrementing field. 增加记录的数量,性能可能开始成为一个问题,因此值得将SELECT限制为前n个(这是正整数)记录,并且为此,请务必为主要增量添加索引领域。

The loop you're asking in point two, would operate on the resultset data, to produce the elaborated result, and the to execute the query update. 您在第二点询问的循环将对结果集数据进行操作,以生成详细结果,并执行查询更新。

$positiveInteger = $_REQUEST['iterations'];
//sanitization...

$sql = 'SELECT id, actions FROM item_counters LIMIT :limit';
$db->connect();
$db->prepare($sql);
$db->bindInteger(':limit',$positiveInteger);
$db->execute();
$rows = $db->fetchAll();

$totalRecords = count($rows);

$incrementValue = intval($positiveInteger / $totalRecords);
$maxIncrementLimit = $positiveInteger % $totalRecords;

$currentRecord = 1;
$results = [];
foreach($rows as $row){
    if($currentRecord <= $maxIncrementLimit){
        $addition = $incrementValue + 1;
    }else{
        $addition = $incrementValue;
    }
    $results[$row['id']] = $row['action'] + $addition;
    $currentRecord++;
}

//then build the query (a little hacky: https://stackoverflow.com/a/27593831/2622455)

$sql = "...";
$db->query($sql);
$db->close();

As far as I can tell, there is a plethora of possibilities, all of which are stupid in one way or another. 据我所知,有太多的可能性,所有这些都是以某种方式愚蠢的。 (I assume PDO, because mysqli is more inconvenient). (我假设PDO,因为mysqli更不方便)。 However, I assume Giacomos solution is among the best solutions, since it's correct, intelligent ... and boring (no offense!). 但是,我认为Giacomos解决方案是最好的解决方案之一,因为它是正确的,聪明的......而且很无聊(没有冒犯!)。 So I provide some other solutions, which are most likely less performant ... 所以我提供了一些其他解决方案,这些解决方案很可能性能较差......

querying and updating like a mad man. 像疯子一样查询和更新。

$increments = 10; // or whatever
$stmt = $db->prepare('UPDATE table SET action = action+1 WHERE id=:id');
while($increments-- > 0) {
    // select the first row with the lowest action value
    // now, this query - apart from the limit - is helpful for all approaches
    $row = $db->query('SELECT id FROM table ORDER BY action ASC, id ASC LIMIT 1')->fetch();
    $stmt->bindValue(':id', $row['id'], PDO::PARAM_INT);
    $stmt->execute();
}

this solution is particularly stupid, because it needs sooo many queries. 这个解决方案特别愚蠢,因为它需要很多查询。 but it's probably correct (minus typos). 但它可能是正确的(减去错别字)。 also, execution time could be increased by not using the same prepared statement over and over again. 另外,通过不反复使用相同的预备语句可以增加执行时间。

stored procedures 存储过程

there's probably a solution with stored procedures or something. 可能有存储过程或其他东西的解决方案。 tbh i never warmed up to stored procedures, so this is essentially a placeholder. tbh我从未预热到存储过程,所以这实际上是一个占位符。

less queries, but still stupid 少查询,但仍然很愚蠢

as long as there are more rows than increments, just increment all rows by 1, then increment the rest. 只要行数多于增量,只需将所有行递增1,然后增加其余行。

$increments = 10;
$rowCount = $db->query('SELECT count(*) FROM table')->fetch()[0];
while($increments >= $rowCount) {
    $db->query('UPDATE table SET action=action+1');
    $increments -= $rowCount;
}
$stmt = $db->prepare('SELECT id FROM table ORDER BY action ASC, id ASC LIMIT :limit');
$stmt->bindValue(':limit', $increments, PDO::PARAM_INT);
$stmt->execute();
// now increase every row's action, from the result set
// yeah, I'm lazy.

even less queries, but still stupid 甚至更少的查询,但仍然是愚蠢的

do the same as before, but add more than just +1, but instead ... +floor(increments/rowcount). 和以前一样,但添加的不仅仅是+1,而是...... + floor(incrementments / rowcount)。 this is essentially, what the next solution does, but it does so more efficiently ... 这基本上就是下一个解决方案的作用,但它的效率更高......

pre-compute target values 预先计算目标值

see Giacomos solution above ... or below, wherever it will be. 看看Giacomos解决方案在......以上,无论它在哪里。

be slightly smarter 稍微聪明一些

with the query I provided before (ordering by action, then by id) you could probably write foreach($rows as $currentRecord => $row){ , and the if clause would say < instead of <= . 使用我之前提供的查询(按操作排序,然后按id),您可以编写foreach($rows as $currentRecord => $row){ ,而if子句会说<而不是<=

compute and find the one id, where the value "flips" 计算并找到一个id,其中值“翻转”

(I assume, this solution is actually the fastest, if done correctly. If really done correctly, this can probably be done in the database alone ... depending on the database and database-foo) (我假设,这个解决方案实际上是最快的,如果做得正确。如果真的做得很好,这可能只在数据库中完成......取决于数据库和数据库-foo)

your table has one invariant ( as far as i can tell , if it doesn't hold, this solution becomes problematic and very confusing): 你的表有一个不变量( 据我所知 ,如果它不成立,这个解决方案会变得有问题而且非常混乱):

there is an id, for which every row with a smaller id has action value x+1 , and every id larger or equal has action value x . 有一个id,每个id较小的行都有动作值x+1 ,每个id大于或等于动作值x

SELECT COUNT(*), action FROM table GROUP BY action

(this should return AT MOST two rows, one for x, one for x+1, with their respective counts, or just one for x or x+1, depending on how you interpret the invariant) (这应该返回AT MOST两行,一个用于x,一个用于x + 1,带有各自的计数,或者只有一个用于x或x + 1,具体取决于你如何解释不变量)

now, you count how many rows have value x and how many have value x+1, then you compute how many y+1 and y you will have at the end. 现在,您计算有多少行具有值x以及有多少行具有值x + 1,然后计算最后将有多少y + 1和y。 now, if your ids are the perfect sequence starting with 1, 2, 3, ... you don't even have to query for the id, because the id == count(y+1). 现在,如果你的id是从1,2,3,...开始的完美序列,你甚至不必查询id,因为id == count(y + 1)。

UPDATE table SET action = IF(id >= :flipid, :y_plus_one, :y)

so this is probably the solution with the least amount of data exchanged between database and php. 所以这可能是数据库和php之间交换数据量最少的解决方案。

now, if you're really smart, instead of the first select you can use this instead: 现在,如果你真的很聪明,而不是第一个选择你可以使用它:

SELECT COUNT(*), SUM(action) FROM table

then add onto the sum, modulo with the count, and the rest is almost as before ^^ 然后加上总和,以计数为模,其余几乎与^^一样

do something more confusing 做一些更令人困惑的事情

do the same as before, but try to use relative amounts ( SET action = action + IF(...) ). 和以前一样,但尝试使用相对金额( SET action = action + IF(...) )。 you'll notice, that you now need up to 2 pivot ids. 你会注意到,你现在需要最多2个枢轴ID。 and that's bat-shit crazy. 那是蝙蝠屎疯了。 don't do it. 不要这样做。 (disclaimer: i can't prove, there's no reason to actually do this, maybe there is...) (免责声明:我无法证明,没有理由这样做,也许有......)

wrap it all up in transactions, because race conditions will ruin your day 将它全部包含在交易中,因为竞争条件会破坏你的一天

the problem in and of itself is not really complicated, when you have no simultaneously acting users. 当你没有同时代理的用户时,问题本身并不复杂。 With concurrent users, you have to wrap it into some transactions, lock some tables to prevent lost updates. 对于并发用户,您必须将其包装到某些事务中,锁定一些表以防止丢失更新。 (Some databases do this by default...) (有些数据库默认执行此操作...)

  1. build a query (A) to choose the first row to update (the hard work is here) 构建一个查询(A)来选择要更新的第一行(这里的辛勤工作)
  2. build a query (B) to update a row 构建一个查询(B)来更新一行
  3. build a query (C) by adding the where clause of the query (A) to the query (B) 通过将查询(A)的where子句添加到查询(B)来构建查询(C)
  4. loop over query (C) has necessary 循环查询(C)是必要的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM