Php 查询Mysql以获取唯一值

Php 查询Mysql以获取唯一值,php,mysql,Php,Mysql,我有一个包含以下详细信息的fuel数据库表 fID是燃油id,vID是车辆id,体积是以升为单位的燃油量,价格是每升燃油的价格,日期是加注车辆的日期,类型是燃油的类型/等级 我想做一个查询,通过将每升燃油成本乘以燃油加注量,再加上另一个距离字段,该字段应为前一个加油表和最近一个加油表之间的差值,来返回燃油成本。在表中,将有很多车辆,因此我只想返回特定车辆的记录。vID将重复,但fID是唯一的 到目前为止,我有下面的查询,但如果数据库中只有一个fuel条目,它将返回空的,而且我无法计算Mysql

我有一个包含以下详细信息的fuel数据库表

fID是燃油id,vID是车辆id,体积是以升为单位的燃油量,价格是每升燃油的价格,日期是加注车辆的日期,类型是燃油的类型/等级

我想做一个查询,通过将每升燃油成本乘以燃油加注量,再加上另一个距离字段,该字段应为前一个加油表和最近一个加油表之间的差值,来返回燃油成本。在表中,将有很多车辆,因此我只想返回特定车辆的记录。vID将重复,但fID是唯一的

到目前为止,我有下面的查询,但如果数据库中只有一个fuel条目,它将返回空的,而且我无法计算Mysql中的成本

Select
t1.*, t2.meter - t1.meter as distance
From fuel t1 left join fuel t2 
on t1.date > t2.date 
where t1.vID = t2.vID 
order by t1.date desc
我该怎么做才对呢

以下是模式:

CREATE TABLE IF NOT EXISTS `fuel` (
  `fID` int(11) NOT NULL AUTO_INCREMENT, 
  `vID` int(11) DEFAULT NULL, 
  `volume` varchar(100) DEFAULT NULL, 
  `price` varchar(100) DEFAULT NULL, 
  `meter` varchar(100) DEFAULT NULL, 
  `date` datetime DEFAULT NULL, 
  `vendorID` int(11) DEFAULT NULL, 
  `notes` text, 
  `type` varchar(50) DEFAULT NULL, 
  PRIMARY KEY (`fID`)
) ENGINE = MyISAM DEFAULT CHARSET = latin1;

INSERT INTO `fuel` (`fID`, `vID`, `volume`, `price`, `meter`, `date`, `vendorID`, `notes`, `type`) 
VALUES 
  (7, 28, '15', '800', '5000', '2018-05-27 05:53:00', NULL, 'Entry number one for this vehicle', 'Unleaded'), 
  (6, 27, '5', '1000', '2000', '2018-05-27 05:50:00', NULL, 'This is the second fill up for this vehicle', 'Leaded'), 
  (5, 27, '15', '1200', '1200', '2018-05-27 04:58:00', NULL, 'Hey there vendors!', 'Leaded'), 
  (9, 26, '25', '750', '4500', '2018-05-27 05:57:00', NULL, NULL, 'Leaded'), 
  (10, 26, '20', '750', '6000', '2018-05-27 05:58:00', NULL, NULL, 'Leaded');
这就是我想要输出将返回的数据的方式。此图以获取车辆vID 27的所有燃油历史记录为例。第一个条目应返回距离0。第二个应该从先前的记录中减去当前的仪表读数,该记录的vID为27,此处为800。。。有什么办法可以做到这一点吗

按照Nick的回答,我用PHP实现了以下内容,但它抛出了一个错误。但是,当我在mysql sql命令中运行它时,它会按预期返回结果

$vID = 27;
$pdo = $this -> con -> query("
     select date_format(f1.date, '%y-%m-%d %H:%i:%s') as date, 
             f1.meter as mileage, 
             case when f2.meter is null then 0 
             else f1.meter - f2.meter end as distance, 
              f1.volume, f1.volume * f1.price as cost from fuel f1 
     left join fuel f2 
     on f2.date = (select max(date) 
     from fuel where fuel.vID = f1.vID and fuel.date < f1.date) 
     where f1.vID = ? order by f1.date ");


if($pdo -> execute([$vID]) && $pdo -> rowCount()) { 
     $res = $pdo -> fetchAll(5); 
     $this -> response($this -> json($res), 200); // send fuel logs 
} else { 
   $this -> response('', 204);  // If no records "No Content" status 
}
下面是我通过php执行代码后得到的错误


此查询将为您提供所需的单个行。查询的工作方式是使用当前加注日期之前此车辆的最新加注日期将燃油连接到自身。如果没有先前的填充日期,则案例表达式将生成距离为0的结果

SELECT DATE_FORMAT(f1.date, '%y-%m-%d %H:%i:%s') AS date, 
  f1.meter AS mileage,
  CASE WHEN f2.meter IS NULL THEN 0
       ELSE f1.meter - f2.meter
       END AS distance,
  f1.volume,
  f1.volume * f1.price AS cost
FROM fuel f1
LEFT JOIN fuel f2
  ON f2.date = (SELECT MAX(date) 
                FROM fuel 
                WHERE fuel.vID = f1.vID AND fuel.date < f1.date)
WHERE f1.vID = 27
ORDER BY f1.date
如果您不想在PHP中对行进行求和,则查询可以生成一个摘要行,只需对查询进行一个小更改,即可添加聚合函数和GROUP BY with ROLLUP子句:

您可以通过日期列为null来检测PHP中的摘要行

Nicks解决方案将起作用,但给定的联接条件是为每个联接运行的子查询。为了达到预期的效果,你也可以试试这个

select f3.date, f3.meter, f3.cost, f3.volume, f3.price, min(f3.distance) as distance from (
  select  f1.date,
    f1.meter,
    case when f2.`meter` is NULL then 0
      else f1.meter - f2.meter end
      as distance,
    (f1.price * f1.volume) as cost,
    f1.`volume`,
    f1.`price`,
    f1.fID
  from fuel f1
  left join fuel f2 
  on f1.vid = f2.vid and f1.`date` > f2.date
  where f1.vid = 27
  group by f1.fID, distance
  order by date, distance asc) f3
group by f3.fID;

@尼克:谢谢你的反馈。希望这个问题能解决

OK SQL也可以实现您想要的,但通过存储过程更直观。我还将提到,控制器和视图,即PHP和jquery,也可以实现您想要的,但这样做要困难得多。在jquery情况下;mysql只需要返回vehcle记录集。 这是密码

-- Proceuodo code
-- create a temporary table
-- set next_mileage = 0;
-- set pre_mileage = 0;
-- get a recordset of the desire vehicle
-- now loop through this recordset based on mileage
    -- if pre_mileage = 0 then pre_mileage = mileage(i);
    -- set next_mileage = mileage(i);
--        calculate distance (by subtracting next_mileage from previous_mileage) and other fields


DELIMITER @@
DROP PROCEDURE get_vehicle_info @@
CREATE PROCEDURE get_vehicle_info
    (IN vid INT)
BEGIN

    DECLARE v_vID BIGINT;
    DECLARE v_volume BIGINT;
    DECLARE v_price BIGINT;
    DECLARE v_meter BIGINT;
    DECLARE v_date datetime;
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur1 CURSOR FOR SELECT vID, volume, price, meter, `date` FROM vehicle_records;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    DROP TEMPORARY TABLE IF EXISTS vehcle_info;
    CREATE TEMPORARY TABLE vehcle_info (`Date` datetime, Mileage BIGINT, Distance BIGINT, Volume BIGINT, Cost BIGINT);
    SET @sqlStr = CONCAT('CREATE OR REPLACE view vehicle_records as SELECT vID, volume, price, meter, `date` FROM fuel WHERE vID = ', vid, ' ORDER BY meter');
    PREPARE stmt FROM @sqlStr;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt; 

    SET @pre_mileage = 0;
    SET @next_mileage = 0;

    OPEN cur1;
    read_loop: LOOP
        FETCH cur1 INTO v_vID, v_volume, v_price, v_meter, v_date;
        IF done THEN
            LEAVE read_loop;
        END IF;

        IF @pre_mileage = 0 THEN
            SET @pre_mileage = v_meter;
        END IF;
        SET @next_mileage = v_meter;

        INSERT INTO vehcle_info VALUES (v_date, v_meter, @next_mileage - @pre_mileage, v_volume, v_price * v_volume);
        SET @pre_mileage = v_meter;
    END LOOP;
    CLOSE cur1;
    SELECT * FROM vehcle_info;

    DROP VIEW IF EXISTS vehicle_records;
    DROP TEMPORARY TABLE vehcle_info;
END @@ 
DELIMITER ; 

CALL get_vehicle_info(27);

请显示更多的示例数据以及预期的输出。如果只有一个条目,mysql无法在t1.date>t2.date进行计算。如果输入>=,可能会得到一个结果。但是一个条目==一个日期,并且一个日期永远不会大于它本身。@TimBiegeleisen我已经用更多的记录更新了图片。如您所见,视频可以重复。所以我想得到,比如说,所有的燃料信息,包括我之前提到的关于vID 27车辆的计算,请发布表格模式和样本数据,而不是图像。你能告诉我们输出是什么样的吗?我仍然不清楚。出于性能原因,在DB级别处理这个问题总是更好。在DB级别处理什么?不是在PHP中循环结果数组并进行计算,而是从查询本身返回所需的结果,如答案所示。@KrishnadasPC这是一个公平的评论,我添加了第二个查询,该查询还将给出摘要行。@Nick当我运行到Mysql命令行时,该查询运行得很好……但当我通过phpy执行此操作时,它会给我一个错误。如果group by子句恰好返回第一行数据,并且同一f3.fID中存在多行数据,则您的查询将只返回正确的数据。由于无法保证这种行为:在这种情况下,服务器可以从每个组中自由选择任何值,因此,除非它们相同,选择的值是不确定的。您无法确定查询将返回正确的结果。我想知道如何修改此查询,以便在同一车辆的driverID发生变化时重置行驶距离。。。例如,一名驾驶员可能被分配到具有vID 27的车辆上,例如两个月,之后他可能被分配到另一辆车辆上。分配给车辆vID 27的新驾驶员不应继续使用前一驾驶员的里程数,新驾驶员应再次从零开始。可以这样做吗?
date                mileage     distance    volume  cost
18-05-27 04:58:00   1200        0           15      18000
18-05-27 05:50:00   2000        800         5       5000
(null)              2000        800         5       23000
select f3.date, f3.meter, f3.cost, f3.volume, f3.price, min(f3.distance) as distance from (
  select  f1.date,
    f1.meter,
    case when f2.`meter` is NULL then 0
      else f1.meter - f2.meter end
      as distance,
    (f1.price * f1.volume) as cost,
    f1.`volume`,
    f1.`price`,
    f1.fID
  from fuel f1
  left join fuel f2 
  on f1.vid = f2.vid and f1.`date` > f2.date
  where f1.vid = 27
  group by f1.fID, distance
  order by date, distance asc) f3
group by f3.fID;
-- Proceuodo code
-- create a temporary table
-- set next_mileage = 0;
-- set pre_mileage = 0;
-- get a recordset of the desire vehicle
-- now loop through this recordset based on mileage
    -- if pre_mileage = 0 then pre_mileage = mileage(i);
    -- set next_mileage = mileage(i);
--        calculate distance (by subtracting next_mileage from previous_mileage) and other fields


DELIMITER @@
DROP PROCEDURE get_vehicle_info @@
CREATE PROCEDURE get_vehicle_info
    (IN vid INT)
BEGIN

    DECLARE v_vID BIGINT;
    DECLARE v_volume BIGINT;
    DECLARE v_price BIGINT;
    DECLARE v_meter BIGINT;
    DECLARE v_date datetime;
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur1 CURSOR FOR SELECT vID, volume, price, meter, `date` FROM vehicle_records;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    DROP TEMPORARY TABLE IF EXISTS vehcle_info;
    CREATE TEMPORARY TABLE vehcle_info (`Date` datetime, Mileage BIGINT, Distance BIGINT, Volume BIGINT, Cost BIGINT);
    SET @sqlStr = CONCAT('CREATE OR REPLACE view vehicle_records as SELECT vID, volume, price, meter, `date` FROM fuel WHERE vID = ', vid, ' ORDER BY meter');
    PREPARE stmt FROM @sqlStr;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt; 

    SET @pre_mileage = 0;
    SET @next_mileage = 0;

    OPEN cur1;
    read_loop: LOOP
        FETCH cur1 INTO v_vID, v_volume, v_price, v_meter, v_date;
        IF done THEN
            LEAVE read_loop;
        END IF;

        IF @pre_mileage = 0 THEN
            SET @pre_mileage = v_meter;
        END IF;
        SET @next_mileage = v_meter;

        INSERT INTO vehcle_info VALUES (v_date, v_meter, @next_mileage - @pre_mileage, v_volume, v_price * v_volume);
        SET @pre_mileage = v_meter;
    END LOOP;
    CLOSE cur1;
    SELECT * FROM vehcle_info;

    DROP VIEW IF EXISTS vehicle_records;
    DROP TEMPORARY TABLE vehcle_info;
END @@ 
DELIMITER ; 

CALL get_vehicle_info(27);