douqiao1413 2016-11-12 00:55
浏览 46

从数组中的特定行获取数据

I have the below SQL query which returns a result containing financial transactions on a loan, made on specific dates...

$data = array( 'loan_id'=>130 );
$STH = $DBH->prepare("SELECT * FROM ledger WHERE loan_id = :loan_id");
$STH->execute($data);
$STH->setFetchMode(PDO::FETCH_ASSOC);
$row = $STH->fetchAll(); 

print_r($row);

This gives the following result

Array ( 
 [0] => Array ( 
        [ledger_id] => 38 
        [loan_id] => 130 
        [ledger_type_id] => 1 
        [amount] => 1.20 
        [ledger_date] => 2016-07-25 
    ) 

 [1] => Array ( 
        [ledger_id] => 39 
        [loan_id] => 130 
        [ledger_type_id] => 3 
        [amount] => 0.90 
        [ledger_date] => 2016-08-15 
    ) 

 [2] => Array ( 
        [ledger_id] => 40 
        [loan_id] => 130 
        [ledger_type_id] => 2 
        [amount] => 0.30 
        [ledger_date] => 2016-09-19 
    ) 
) 

I now need to loop through all dates that the loan has been active, and find the 'ledger_type_id' and 'amount' for transactions that occurred on the current date in the loop. The ledger table does not contain a record for every day, however I need to look at each individual day that the loan has been active for other purposes not relevant to this question.

$start_date = new DateTime('2016-07-13');
$today = new DateTime();
$today = $today->modify( '+1 day' ); // add one day to include today
$interval = new DateInterval('P1D'); 
$daterange = new DatePeriod($start_date, $interval ,$today);

foreach ($daterange as $date) {

    /*  
       The above SQL query returns a row with the 'ledger_date' 
       of '2016-08-15'

       I need to find the value of the ledger_type_id 
       for the same row from the above query 
       when $date = '2016-08-15' in this loop
    */

    // Do other unrelated stuff on each day of the loop.....

}

I have tried a few things using in_array() but without success.

  • 写回答

1条回答 默认 最新

  • doufu9145 2016-11-16 14:18
    关注

    When you're working in SQL and you think I need a loop, that should have you think, wait a minute. SQL is declarative, not procedural.

    In SQL, the question of handling every day in a date range is usually done with a table (physical or virtual) containing all the days. We can call it the calendar table. Then we can LEFT JOIN the table with actual data to that table. In your example:

    SELECT calendar.day, ledger.*
      FROM calendar
      LEFT JOIN ledger ON calendar.day = ledger.date
     WHERE loan_id = :loan_id
     ORDER BY calendar.day, ledger.id
    

    This will give a result set with at least one row for each calendar day.

    The trick is to get an appropriate calendar table. Here's a way to do that.

                 SELECT mintime + INTERVAL seq.seq DAY AS day
                   FROM (
                           SELECT MIN(DATE(ledger.date)) AS mintime,
                                  MAX(DATE(ledger.date)) AS maxtime
                             FROM ledger
                        ) AS minmax
                   JOIN seq_0_to_999999 AS seq 
                     ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
    

    This will give you a virtual table (subquery) covering the range of dates in your ledger.

    So you query will look like this:

    SELECT calendar.day, ledger.*
      FROM (
                 SELECT mintime + INTERVAL seq.seq DAY AS day
                   FROM (
                           SELECT MIN(DATE(ledger.date)) AS mintime,
                                  MAX(DATE(ledger.date)) AS maxtime
                             FROM ledger
                        ) AS minmax
                   JOIN seq_0_to_999999 AS seq 
                     ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
           ) calendar
      LEFT JOIN ledger ON ledger.date >= calendar.day
                      AND ledger.date < calendar.day + INTERVAL 1 DAY
     WHERE loan_id = :loan_id
     ORDER BY calendar.day, ledger.id
    

    If you happen to be using the MariaDB fork of MySQL, the handy-dandy table seq_0_to_999999 is predefined for you as a sequence table. Otherwise you can create it easily as a series of views, like this:

    DROP TABLE IF EXISTS seq_0_to_9;
    CREATE TABLE seq_0_to_9 AS
       SELECT 0 AS seq UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
        UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9;
    DROP VIEW IF EXISTS seq_0_to_999;
    CREATE VIEW seq_0_to_999 AS (
    SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
      FROM seq_0_to_9 a
      JOIN seq_0_to_9 b
      JOIN seq_0_to_9 c
    );
    DROP VIEW IF EXISTS seq_0_to_999999;
    CREATE VIEW seq_0_to_999999 AS (
    SELECT (a.seq + (1000 * b.seq)) AS seq
      FROM seq_0_to_999 a
      JOIN seq_0_to_999 b
    );
    

    I have more on this topic at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/

    评论

报告相同问题?

悬赏问题

  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序
  • ¥15 多址通信方式的抗噪声性能和系统容量对比
  • ¥15 winform的chart曲线生成时有凸起
  • ¥15 msix packaging tool打包问题
  • ¥15 finalshell节点的搭建代码和那个端口代码教程
  • ¥15 Centos / PETSc / PETGEM
  • ¥15 centos7.9 IPv6端口telnet和端口监控问题
  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作