Каков наиболее эффективный способ расчета промежуточного итога/баланса при использовании нумерации страниц (PHP, MySQL)

У меня есть таблица MySQL, в которой хранятся записи о пробеге, зарегистрированные сотрудниками. У меня есть страница PHP, которая выводит записи о пробеге для каждого сотрудника в таблице (от самых новых до самых старых) вместе с текущим балансом. Это все работает нормально.

Записи о пробеге растут быстрее, чем ожидалось, и теперь стало очевидно, что требуется нумерация страниц. Я запрограммировал разбиение на страницы, и это отлично работает. Проблема, с которой я столкнулся сейчас, заключается в том, что разбивка на страницы «нарушила» текущий баланс таким образом, что она игнорирует любые значения за пределами выбранных мной записей. Например, представьте, что ваш банковский счет только что предоставил баланс за июль, не принимая во внимание то, что было на вашем счете в июне.

Предложения о том, как решить эту проблему в теории (мне не нужен построчный код), будут высоко оценены, поскольку Google не оказывает большой помощи.

ИЗМЕНИТЬ 1

Database and data

-- phpMyAdmin SQL Dump
-- version 4.0.10.14
-- http://www.phpmyadmin.net
--
-- Host: localhost:3306
-- Generation Time: Jul 21, 2016 at 07:11 PM
-- Server version: 5.1.73-cll
-- PHP Version: 5.4.31

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

--
-- Database: `propsyst_main`
--

-- --------------------------------------------------------

--
-- Table structure for table `employee_mileage`
--

CREATE TABLE IF NOT EXISTS `employee_mileage` (
  `employee_mileage_id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_mileage_employee_id` int(11) DEFAULT NULL,
  `employee_mileage_vehicle_id` smallint(6) DEFAULT NULL,
  `employee_mileage_start_postcode` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `employee_mileage_end_postcode` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `employee_mileage_mileage` decimal(6,2) DEFAULT NULL,
  PRIMARY KEY (`employee_mileage_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=8 ;

--
-- Dumping data for table `employee_mileage`
--

INSERT INTO `employee_mileage` (`employee_mileage_id`, `employee_mileage_employee_id`, `employee_mileage_vehicle_id`, `employee_mileage_start_postcode`, `employee_mileage_end_postcode`, `employee_mileage_mileage`) VALUES
(1, 1, 2, 'L17 0BZ', 'L36 9TJ', '1.00'),
(2, 1, 2, 'L17 0BZ', 'L36 9TJ', '2.00'),
(3, 1, 2, 'L17 0BZ', 'L36 9TJ', '3.00'),
(4, 1, 2, 'L17 0BZ', 'L36 9TJ', '4.00'),
(5, 1, 2, 'L17 0BZ', 'L36 9TJ', '5.00'),
(6, 1, 2, 'L17 0BZ', 'L36 9TJ', '6.00'),
(7, 1, 2, 'L17 0BZ', 'L36 9TJ', '7.00');

ИЗМЕНИТЬ 2

Попытка запроса, которая не работает;

$statement = "SELECT *
from (
        SELECT     em.*, e.*,
                   @balance := @balance + em.employee_mileage_mileage as balance
        FROM       employee_mileage em
        CROSS JOIN (select   @balance := 0) init
        INNER JOIN employee e
on em.employee_mileage_employee_id = e.employee_id
        where      em.employee_mileage_employee_id = " . $employee_id . "
        order by   em.employee_mileage_id
        ) as base
ORDER BY   em.employee_mileage_id DESC";

$employee_mileage_query = mysqli_query($con,"{$statement} LIMIT {$startpoint} , {$per_page}") or die(mysql_error());

ИЗМЕНИТЬ 3

База данных;

-- phpMyAdmin SQL Dump
-- version 4.0.10.14
-- http://www.phpmyadmin.net
--
-- Host: localhost:3306
-- Generation Time: Jul 25, 2016 at 10:22 PM
-- Server version: 5.1.73-cll
-- PHP Version: 5.4.31

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

--
-- Database: `propsyst_main`
--

-- --------------------------------------------------------

--
-- Table structure for table `employee_mileage`
--

CREATE TABLE IF NOT EXISTS `employee_mileage` (
  `employee_mileage_id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_mileage_employee_id` int(11) DEFAULT NULL,
  `employee_mileage_vehicle_id` smallint(6) DEFAULT NULL,
  `employee_mileage_journey_date` date DEFAULT NULL,
  `employee_mileage_start_postcode` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `employee_mileage_end_postcode` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `employee_mileage_mileage` decimal(6,2) DEFAULT NULL,
  `employee_mileage_date_created` datetime DEFAULT NULL,
  `employee_mileage_created_by` int(11) DEFAULT NULL,
  PRIMARY KEY (`employee_mileage_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=7 ;

--
-- Dumping data for table `employee_mileage`
--

INSERT INTO `employee_mileage` (`employee_mileage_id`, `employee_mileage_employee_id`, `employee_mileage_vehicle_id`, `employee_mileage_journey_date`, `employee_mileage_start_postcode`, `employee_mileage_end_postcode`, `employee_mileage_mileage`, `employee_mileage_date_created`, `employee_mileage_created_by`) VALUES
(1, 32, 1, '2016-07-15', 'L17 0BZ', 'L6 5BJ', '4.19', '2016-07-25 10:15:01', 32),
(2, 32, 1, '2016-07-15', 'L6 5BJ', 'L17 0BZ', '4.19', '2016-07-25 10:15:01', 32),
(5, 32, 1, '2016-07-23', 'L17 0BZ', 'L17 1AE', '1.55', '2016-07-25 12:14:15', 32),
(3, 32, 1, '2016-07-21', 'L17 0BZ', 'L19 0PD', '2.03', '2016-07-25 12:09:24', 32),
(4, 32, 1, '2016-07-21', 'L19 0PD', 'L17 0BZ', '2.03', '2016-07-25 12:09:24', 32),
(6, 32, 1, '2016-07-23', 'L17 1AE', 'L17 0BZ', '1.55', '2016-07-25 12:14:15', 32);

Запрос;

$statement = "SELECT *
from (
        SELECT     em.*, e.*, v.*,
                   @balance := @balance + em.employee_mileage_mileage as balance
        FROM       employee_mileage em
        CROSS JOIN (select @balance := 0) init
        INNER JOIN employee e
        on em.employee_mileage_employee_id = e.employee_id
        INNER JOIN vehicle v
        on em.employee_mileage_vehicle_id = v.vehicle_id
        WHERE      em.employee_mileage_employee_id = " . $employee_id . "
        ORDER BY   em.employee_mileage_id
        ) as base
ORDER BY base.employee_mileage_id DESC";

$employee_mileage_query = mysqli_query($con,"{$statement} LIMIT {$startpoint} , {$per_page}") or die(mysql_error());

Выход;

введите здесь описание изображения


person Michael LB    schedule 21.07.2016    source источник
comment
Использовать SQL SUM для баланса?   -  person Rael Gugelmin Cunha    schedule 21.07.2016
comment
@RaelGugelminCunha Это не работает, потому что запрос к базе данных выбирает только, скажем, 30 записей на страницу.   -  person Michael LB    schedule 21.07.2016
comment
Насколько я понимаю, ваша разбивка на страницы заключается в том, что вы предварительно формируете запрос выбора для 30 записей каждый раз, когда пользователь запрашивает текущую страницу. Чтобы получить все записи, либо используйте сумму по всем записям за этот месяц, либо выберите все записи, рассчитайте сумму, но покажите пользователю только первые 30. Когда пользователь затем выбирает загрузку следующих 30 из выбранного запроса.   -  person Nyranith    schedule 21.07.2016
comment
@MichaelLB, используйте отдельные запросы для баланса и записи для страницы.   -  person Deepak Chaudhary    schedule 21.07.2016
comment
@Nyranith Извините, может быть, мой вопрос должен быть более ясным. Пагинация работает нормально, проблема в расчете текущего баланса.   -  person Michael LB    schedule 21.07.2016
comment
баланс рассчитывается с начала времен или есть сбросы, типа каждый месяц или год?   -  person trincot    schedule 21.07.2016
comment
@trincot Да, с незапамятных времен, я думаю, это то, чего всем не хватает   -  person Michael LB    schedule 21.07.2016


Ответы (1)


Я бы предложил рассчитать текущий баланс в вашем SQL-запросе.

Вот пример того, как может выглядеть это выражение SQL:

SELECT *
from (
        SELECT     *,
                   @balance := @balance + em.employee_mileage_mileage as balance
        FROM       (select     em.*, e.*, v.*
                    from       employee_mileage em
                    INNER JOIN employee e
                            on em.employee_mileage_employee_id = e.employee_id
                    INNER JOIN vehicle v
                            on em.employee_mileage_vehicle_id = v.vehicle_id
                    WHERE      em.employee_mileage_employee_id = ?
                    ORDER BY   em.employee_mileage_id ASC
                    ) em
        CROSS JOIN (select @balance := 0) init
        ) as base
ORDER BY base.employee_mileage_id DESC
LIMIT   ?, 2

Параметры в приведенном выше выражении (отмеченные ?) должны быть привязаны к:

  • employee_id, для которого вы хотите отобразить данные
  • начальная строка текущей страницы (отсчитывается от нуля)

Внутренний запрос просматривает все строки для этого конкретного сотрудника и добавляет текущий баланс к каждой записи. Затем внешний запрос меняет порядок сортировки на обратный и применяет к нему ограничение для целей разбиения по страницам.

Это решение не требует вычислений в PHP; Текущий баланс легко доступен в наборе результатов запроса и верен на каждой странице.

Вот небольшой скрипт SQL, демонстрирующий получение второй страницы с двумя строками на странице. , из общего набора 6 рядов, включая правильный баланс.

person trincot    schedule 21.07.2016
comment
Большое спасибо за вашу помощь. Я опубликовал свою структуру таблицы, образцы данных и попытку использовать ваш запрос, но запрос возвращает нулевые результаты. Вы можете помочь? - person Michael LB; 21.07.2016
comment
Не могли бы вы взглянуть на Edit 3, запрос, похоже, не работает с моими реальными данными... - person Michael LB; 26.07.2016