Php 付费会员服务

Php 付费会员服务,php,mysql,Php,Mysql,我有两个表,一个是事务表,一个是成员表 一个用户可以一次激活所有可用的成员资格,每个成员资格提供不同的好处 我正在尝试处理具有相同用户id和相同成员身份的多个事务记录 这是我的桌子 Transactions table ID | USERID | MID | CREATED | AMOUNT ------------------------------------------------- 1 | 1 | 2 | 2014-10-01 00:00:00 |

我有两个表,一个是事务表,一个是成员表

一个用户可以一次激活所有可用的成员资格,每个成员资格提供不同的好处

我正在尝试处理具有相同用户id和相同成员身份的多个事务记录

这是我的桌子

Transactions table

ID | USERID | MID |      CREATED      | AMOUNT
-------------------------------------------------
 1  |   1   |  2  | 2014-10-01 00:00:00 |   1
 2  |   1   |  2  | 2014-10-16 00:00:00 |   1
 3  |   2   |  1  | 2014-10-30 00:00:00 |   1

Membership tables

ID |    TITLE    |  DURATION
-------------------------
 1 |   Premium   |     365
 2 |  Supporter  |     30
 3 | Beta Access |     30
在transactions表中,我有两条userid 1的记录,一条始于2014-10-01,另一条始于2014-10-01 2014年10月16日

以下脚本可以很好地选择各个活动成员身份日志

SELECT t.USERID AS UID, t.CREATED AS CREATED, FROM_UNIXTIME(UNIX_TIMESTAMP(t.CREATED) + t.AMOUNT * m.DURATION) AS ENDS
FROM Transactions AS t
LEFT JOIN Memberships AS m on m.ID = t.MID
LIMIT 5
输出是这样的

UID | MID |       CREATED      | ENDS
-----------------------------------------------------
 1  | 2  | 2014-10-01 00:00:00 | 2014-10-31 00:00:00
 1  | 2  | 2014-10-16 00:00:00 | 2014-11-15 00:00:00
 2  | 1  | 2014-10-30 00:00:00 | 2015-10-30 00:00:00
现在有两条记录具有相同的成员ID MID和用户ID UID,第一条记录在第二条记录之前过期

基本上,我所要做的就是“合并”或“合并”一个memberishp的总“未使用”天数,只要在当前用户id和membership id过期之前添加了另一个成员id

以下是显示给定数据和所需输出的示例:

ID | USERID | MID |      CREATED        | Amount
-------------------------------------------------
 1  |   1   |  2  | 2014-10-01 00:00:00 |   1 #30 days days remains
 2  |   1   |  2  | 2014-10-17 00:00:00 |   1 #14 days of the previous transaction is not fully consumed,43 days remains - (days amount +  previous unused days)
 3  |   1   |  2  | 2014-11-01 00:00:00 |   1 #28 days of the previous transaction (44 days ones) is not fully consumed,59 days remains - (days amount +  previous unused days)
 4  |   2   |  3  | 2014-10-01 00:00:00 |   1 #30 days days remains
 5  |   2   |  3  | 2014-11-08 00:00:00 |   1 #30 days days remains
输出应该是这样的

UID | MID |       CREATED       | ENDS
-----------------------------------------------------
 1  | 2  | 2015-10-01 00:00:00 | 2014-12-29 00:00:00
 2  | 1  | 2014-10-01 00:00:00 | 2014-10-30 00:00:00
 2  | 1  | 2014-11-08 00:00:00 | 2014-12-08 00:00:00
如果不清楚的话,我很抱歉,因为英语不是我的母语,也没有语言来解释我要完成的任务

编辑:
如果不可能通过mysql寻找php解决方案。

我想说,主要有三种不同的方法,我尝试给出这些方法的示例。然而,所有方法都有优点/缺点

MySQL查询

这样的查询似乎需要某种递归。。。因此,一种可能的方法可以与之类似,通过在select语句中使用来利用MySQL的表达式求值

返回每个事务记录的持续时间和扩展结束时间的查询:

SELECT id, userid, mid, created,
  -- initialize if new userid or membership id
  IF (@lastuser!=userid or @lastmb!=mid, (@prevend:=created)+(@lastuser:=userid)+(@lastmb:=mid), 0) AS tmp,
  -- calculate unused days
  @unused:=IF(@prevend>created, datediff(@prevend, created), 0) AS tmp2, 
  -- calculate the end of current membership (will be used for next record)
  @prevend:=DATE_ADD(created, INTERVAL (amount * duration)+@unused DAY) AS ends,
  -- calculate the days remaining
  @unused+duration AS 'days remain'
FROM (
  SELECT tt.id, tt.userid, tt.mid, tt.created, tt.amount, duration
  FROM transactions tt
  LEFT JOIN memberships as m on m.ID = tt.MID
  ORDER BY tt.userid, tt.created) t
JOIN (SELECT @lastuser:=0)tmp;
此查询的输出为:

id  userid mid  created             tmp     tmp2 ends                   days remain
1   1      2    2014-10-01 00:00:00 2017    0    2014-10-31 00:00:00    30
2   1      2    2014-10-17 00:00:00 0       14   2014-11-30 00:00:00    44
3   1      2    2014-11-01 00:00:00 0       29   2014-12-30 00:00:00    59
4   2      3    2014-10-01 00:00:00 2019    0    2014-10-31 00:00:00    30
5   2      3    2014-11-08 00:00:00 0       0    2014-12-08 00:00:00    30
另一项任务是仅输出合并的间隔:

SELECT userid, mid, begins, max(ends) as ends FROM (
    SELECT id, userid, mid, created,
      -- initialize if new userid or membership id
      IF (@lastuser!=userid or @lastmb!=mid, (@prevend:=created)+(@lastuser:=userid)+(@lastmb:=mid), 0) AS tmp,
      -- firstcreated stores the very first creation time of overlapping memberships 
      if (@prevend>created, @firstcreated, (@firstcreated:=created)) as begins,
      -- calculate unused days
      @unused:=IF(@prevend>created, datediff(@prevend, created), 0) AS tmp2, 
      -- calculate the end of current membership (will be used for next record)
      @prevend:=DATE_ADD(created, INTERVAL (amount * duration)+@unused DAY) AS ends,
      -- calculate the days remaining
      @unused+duration AS 'days remain'
    FROM (
      SELECT tt.id, tt.userid, tt.mid, tt.created, tt.amount, duration
      FROM transactions tt
      LEFT JOIN memberships as m on m.ID = tt.MID
      ORDER BY tt.userid, tt.created) t
    JOIN (SELECT @lastuser:=0)tmp
) mship 
GROUP BY userid, mid, begins;
但是,请注意,这确实不是一个可取的解决方案,因为表达式的求值顺序无法保证。因此,查询可能会产生一个好结果,但是对于不同的数据集,或者对于不同的MySQL版本,它很容易产生一个坏结果。在建议的查询中,有一个子查询带有ORDERBY子句,因此记录顺序在这里不应该成为问题,但是如果您想将此查询放入希望维护更长时间的代码中,例如,当您迁移到新版本的MySQL时,您可能会感到惊讶

至少,它似乎也在MySQL 5.5和MySQL 5.6上工作

所以再次提醒大家,因为正如MySQL文档所说:

一般来说,除了SET语句之外,您不应该 为用户变量赋值并读取同一变量中的值 陈述例如,要增加变量,可以这样做:

设置@a=@a+1;对于其他语句,例如SELECT,您可能会得到 你期望的结果,但这不是保证。在下面 语句,您可能会认为MySQL将首先计算@a,然后再计算@a 第二次做作业:

选择@a,@a:=@a+1

但是,涉及用户变量的表达式的求值顺序 没有定义

计算客户端应用程序端的所有内容,例如PHP

想法是一样的。获取按用户ID、mid和创建日期排序的事务。对记录进行迭代,对于每个新事务,如果有“未使用”的日期,则使用“未使用”的日期延长成员资格的持续时间,该日期可以从以前的事务中计算出来。当我们看到会员资格出现中断时,我们会保存实际期限

执行此操作的示例PHP代码:

<?php
$conn = mysqli_connect("localhost", "user", "password", "db");
if (mysqli_connect_errno($conn)) {
    echo "Failed to connect to MySQL: " . mysqli_connect_error();
}

// Query to select membership information
$res = mysqli_query($conn, "select t.id, userid, mid, created, (m.duration * t.amount) as duration
    from transactions t
    left join memberships m
    on t.mid=m.id
    order by userid, mid, created");

echo "Temporary calculation:<br/>";
echo "<table border=\"1\">";
echo "<th>Id</th><th>UserId</th><th>MID</th><th>Created</th><th>Unused</th><th>End</th><th>Duration</th>";

$last_userid=0;
while ($row = $res->fetch_assoc()) {
    // Beginning of a new userid or membership id
    if ($row['userid']!=$last_userid or $row['mid']!=$last_mid) {
        // If we are not at the first record, we save the current period
        if ($last_userid!=0) {
            $mships[$last_userid][$last_mid][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
        }

        // Initialize temporaries
        $last_userid=$row['userid'];
        $last_mid=$row['mid'];
        $first_created=new DateTime($row['created']);
        $last_end=clone $first_created;
    }

    // Calculate duration
    $created=new DateTime($row['created']);
    $unused=date_diff($created, $last_end);
    $ends=clone $created;
    $ends->add(new DateInterval("P".$row['duration']."D"));

    // $unused->invert is 1 if diff is negative 
    if ($unused->invert==0 && $unused->days>=0) {
        // This transaction extends/immediately follows the previous period
        $ends->add(new DateInterval('P'.$unused->days.'D'));
    } else {
        // We split the period -> save it!
        $mships[$row['userid']][$row['mid']][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
        $first_created=new DateTime($row['created']);
    }

    $duration=date_diff($ends, $created);   

    echo "<tr>";
    echo "<td>",$row['id'],"</td>";
    echo "<td>",$row['userid'],"</td>";
    echo "<td>",$row['mid'],"</td>";
    echo "<td>",$row['created'],"</td>";
    echo "<td>",($unused->invert==0 ? $unused->format('%a') : 0),"</td>";
    echo "<td>",$ends->format('Y-m-d H:i:s'),"</td>";
    echo "<td>",$duration->format('%a'),"</td>";
    echo "</tr>";

    $last_end=$ends;
}
// Last period should be saved
if ($last_userid!=0) {
    $mships[$last_userid][$last_mid][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
}

echo "</table><br/>";

echo "Final array:<br/>";
echo "<table border=\"1\">";
echo "<th>UserId</th><th>MID</th><th>Created</th><th>End</th>";

foreach ($mships as $uid => &$mids) {
    foreach ($mids as $mid => &$periods) {
        foreach ($periods as $begin => $end) {
            echo "<tr>";
            echo "<td>",$uid,"</td>";
            echo "<td>",$mid,"</td>";
            echo "<td>",$begin,"</td>";
            echo "<td>",$end,"</td>";
            echo "</tr>";               
        }
    }
}

$conn->close();

?>
MySQL存储过程

还可以使用存储过程计算结果集。例如,使用与前面PHP代码相同的算法的示例过程

DROP PROCEDURE IF EXISTS get_memberships;

delimiter //

CREATE PROCEDURE get_memberships()
BEGIN
  DECLARE done INT DEFAULT FALSE;
  DECLARE uid, mid, duration INT;
  DECLARE created, unused, first_created, ends TIMESTAMP;
  -- make sure that there is no user with 0 id
  DECLARE last_uid, last_mid INT DEFAULT 0;
  DECLARE last_end TIMESTAMP;
  DECLARE cur CURSOR FOR SELECT t.userid, t.mid, t.created, (m.duration * t.amount) as duration
    FROM transactions t
    LEFT JOIN memberships m
    ON t.mid=m.id
    ORDER BY userid, mid, created;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN cur;

  REPEAT
    FETCH cur INTO uid, mid, created, duration;
    IF (!done) THEN
        IF (uid!=last_uid OR last_mid!=mid) THEN
            IF (last_uid!=0) THEN
                INSERT INTO results (userid, mid, created, ends) VALUES (last_uid, last_mid, first_created, last_end);
            END IF;

            SET last_uid = uid;
            SET last_mid = mid;
            SET last_end = created;
            SET first_created = created;
        END IF;

        SET ends = DATE_ADD(created, INTERVAL duration DAY);

        IF (last_end>=created) THEN
            SET ends = DATE_ADD(ends, INTERVAL datediff(last_end, created) DAY);
        ELSE
            INSERT INTO results (userid, mid, created, ends) VALUES (uid, mid, first_created, last_end);
            SET first_created = created;
        END IF;

        SET last_end = ends;
    END IF;
  UNTIL done
  END REPEAT;

  IF (last_uid!=0) THEN
    INSERT INTO results (userid, mid, created, ends) VALUES (uid, last_mid, first_created, last_end);
  END IF;

  CLOSE cur;
END
//

DROP TABLE IF EXISTS results //
CREATE TEMPORARY TABLE results AS SELECT userid, mid, created, created as ends FROM transactions WHERE 0 //
call get_memberships //
SELECT * FROM results //
DROP TABLE results;

但是,这种技术的一个缺点是使用临时表。

我想说,主要有三种不同的方法,我尝试给出这些方法的示例。然而,所有方法都有优点/缺点

MySQL查询

这样的查询似乎需要某种递归。。。因此,一种可能的方法可以与之类似,通过在select语句中使用来利用MySQL的表达式求值

返回每个事务记录的持续时间和扩展结束时间的查询:

SELECT id, userid, mid, created,
  -- initialize if new userid or membership id
  IF (@lastuser!=userid or @lastmb!=mid, (@prevend:=created)+(@lastuser:=userid)+(@lastmb:=mid), 0) AS tmp,
  -- calculate unused days
  @unused:=IF(@prevend>created, datediff(@prevend, created), 0) AS tmp2, 
  -- calculate the end of current membership (will be used for next record)
  @prevend:=DATE_ADD(created, INTERVAL (amount * duration)+@unused DAY) AS ends,
  -- calculate the days remaining
  @unused+duration AS 'days remain'
FROM (
  SELECT tt.id, tt.userid, tt.mid, tt.created, tt.amount, duration
  FROM transactions tt
  LEFT JOIN memberships as m on m.ID = tt.MID
  ORDER BY tt.userid, tt.created) t
JOIN (SELECT @lastuser:=0)tmp;
此查询的输出为:

id  userid mid  created             tmp     tmp2 ends                   days remain
1   1      2    2014-10-01 00:00:00 2017    0    2014-10-31 00:00:00    30
2   1      2    2014-10-17 00:00:00 0       14   2014-11-30 00:00:00    44
3   1      2    2014-11-01 00:00:00 0       29   2014-12-30 00:00:00    59
4   2      3    2014-10-01 00:00:00 2019    0    2014-10-31 00:00:00    30
5   2      3    2014-11-08 00:00:00 0       0    2014-12-08 00:00:00    30
另一项任务是仅输出合并的间隔:

SELECT userid, mid, begins, max(ends) as ends FROM (
    SELECT id, userid, mid, created,
      -- initialize if new userid or membership id
      IF (@lastuser!=userid or @lastmb!=mid, (@prevend:=created)+(@lastuser:=userid)+(@lastmb:=mid), 0) AS tmp,
      -- firstcreated stores the very first creation time of overlapping memberships 
      if (@prevend>created, @firstcreated, (@firstcreated:=created)) as begins,
      -- calculate unused days
      @unused:=IF(@prevend>created, datediff(@prevend, created), 0) AS tmp2, 
      -- calculate the end of current membership (will be used for next record)
      @prevend:=DATE_ADD(created, INTERVAL (amount * duration)+@unused DAY) AS ends,
      -- calculate the days remaining
      @unused+duration AS 'days remain'
    FROM (
      SELECT tt.id, tt.userid, tt.mid, tt.created, tt.amount, duration
      FROM transactions tt
      LEFT JOIN memberships as m on m.ID = tt.MID
      ORDER BY tt.userid, tt.created) t
    JOIN (SELECT @lastuser:=0)tmp
) mship 
GROUP BY userid, mid, begins;
但是,请注意,这确实不是一个可取的解决方案,因为表达式的求值顺序无法保证。因此,查询可能会产生一个好结果,但是对于不同的数据集,或者对于不同的MySQL版本,它很容易产生一个坏结果。在建议的查询中,有一个子查询带有ORDERBY子句,因此记录顺序在这里不应该成为问题,但是如果您想将此查询放入希望维护更长时间的代码中,例如,当您迁移到新版本的MySQL时,您可能会感到惊讶

在乐 ast,它似乎也在MySQL 5.5和MySQL 5.6上工作

所以再次提醒大家,因为正如MySQL文档所说:

一般来说,除了SET语句之外,您不应该 为用户变量赋值并读取同一变量中的值 陈述例如,要增加变量,可以这样做:

设置@a=@a+1;对于其他语句,例如SELECT,您可能会得到 你期望的结果,但这不是保证。在下面 语句,您可能会认为MySQL将首先计算@a,然后再计算@a 第二次做作业:

选择@a,@a:=@a+1

但是,涉及用户变量的表达式的求值顺序 没有定义

计算客户端应用程序端的所有内容,例如PHP

想法是一样的。获取按用户ID、mid和创建日期排序的事务。对记录进行迭代,对于每个新事务,如果有“未使用”的日期,则使用“未使用”的日期延长成员资格的持续时间,该日期可以从以前的事务中计算出来。当我们看到会员资格出现中断时,我们会保存实际期限

执行此操作的示例PHP代码:

<?php
$conn = mysqli_connect("localhost", "user", "password", "db");
if (mysqli_connect_errno($conn)) {
    echo "Failed to connect to MySQL: " . mysqli_connect_error();
}

// Query to select membership information
$res = mysqli_query($conn, "select t.id, userid, mid, created, (m.duration * t.amount) as duration
    from transactions t
    left join memberships m
    on t.mid=m.id
    order by userid, mid, created");

echo "Temporary calculation:<br/>";
echo "<table border=\"1\">";
echo "<th>Id</th><th>UserId</th><th>MID</th><th>Created</th><th>Unused</th><th>End</th><th>Duration</th>";

$last_userid=0;
while ($row = $res->fetch_assoc()) {
    // Beginning of a new userid or membership id
    if ($row['userid']!=$last_userid or $row['mid']!=$last_mid) {
        // If we are not at the first record, we save the current period
        if ($last_userid!=0) {
            $mships[$last_userid][$last_mid][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
        }

        // Initialize temporaries
        $last_userid=$row['userid'];
        $last_mid=$row['mid'];
        $first_created=new DateTime($row['created']);
        $last_end=clone $first_created;
    }

    // Calculate duration
    $created=new DateTime($row['created']);
    $unused=date_diff($created, $last_end);
    $ends=clone $created;
    $ends->add(new DateInterval("P".$row['duration']."D"));

    // $unused->invert is 1 if diff is negative 
    if ($unused->invert==0 && $unused->days>=0) {
        // This transaction extends/immediately follows the previous period
        $ends->add(new DateInterval('P'.$unused->days.'D'));
    } else {
        // We split the period -> save it!
        $mships[$row['userid']][$row['mid']][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
        $first_created=new DateTime($row['created']);
    }

    $duration=date_diff($ends, $created);   

    echo "<tr>";
    echo "<td>",$row['id'],"</td>";
    echo "<td>",$row['userid'],"</td>";
    echo "<td>",$row['mid'],"</td>";
    echo "<td>",$row['created'],"</td>";
    echo "<td>",($unused->invert==0 ? $unused->format('%a') : 0),"</td>";
    echo "<td>",$ends->format('Y-m-d H:i:s'),"</td>";
    echo "<td>",$duration->format('%a'),"</td>";
    echo "</tr>";

    $last_end=$ends;
}
// Last period should be saved
if ($last_userid!=0) {
    $mships[$last_userid][$last_mid][$first_created->format('Y-m-d H:i:s')]=$last_end->format('Y-m-d H:i:s');
}

echo "</table><br/>";

echo "Final array:<br/>";
echo "<table border=\"1\">";
echo "<th>UserId</th><th>MID</th><th>Created</th><th>End</th>";

foreach ($mships as $uid => &$mids) {
    foreach ($mids as $mid => &$periods) {
        foreach ($periods as $begin => $end) {
            echo "<tr>";
            echo "<td>",$uid,"</td>";
            echo "<td>",$mid,"</td>";
            echo "<td>",$begin,"</td>";
            echo "<td>",$end,"</td>";
            echo "</tr>";               
        }
    }
}

$conn->close();

?>
MySQL存储过程

还可以使用存储过程计算结果集。例如,使用与前面PHP代码相同的算法的示例过程

DROP PROCEDURE IF EXISTS get_memberships;

delimiter //

CREATE PROCEDURE get_memberships()
BEGIN
  DECLARE done INT DEFAULT FALSE;
  DECLARE uid, mid, duration INT;
  DECLARE created, unused, first_created, ends TIMESTAMP;
  -- make sure that there is no user with 0 id
  DECLARE last_uid, last_mid INT DEFAULT 0;
  DECLARE last_end TIMESTAMP;
  DECLARE cur CURSOR FOR SELECT t.userid, t.mid, t.created, (m.duration * t.amount) as duration
    FROM transactions t
    LEFT JOIN memberships m
    ON t.mid=m.id
    ORDER BY userid, mid, created;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN cur;

  REPEAT
    FETCH cur INTO uid, mid, created, duration;
    IF (!done) THEN
        IF (uid!=last_uid OR last_mid!=mid) THEN
            IF (last_uid!=0) THEN
                INSERT INTO results (userid, mid, created, ends) VALUES (last_uid, last_mid, first_created, last_end);
            END IF;

            SET last_uid = uid;
            SET last_mid = mid;
            SET last_end = created;
            SET first_created = created;
        END IF;

        SET ends = DATE_ADD(created, INTERVAL duration DAY);

        IF (last_end>=created) THEN
            SET ends = DATE_ADD(ends, INTERVAL datediff(last_end, created) DAY);
        ELSE
            INSERT INTO results (userid, mid, created, ends) VALUES (uid, mid, first_created, last_end);
            SET first_created = created;
        END IF;

        SET last_end = ends;
    END IF;
  UNTIL done
  END REPEAT;

  IF (last_uid!=0) THEN
    INSERT INTO results (userid, mid, created, ends) VALUES (uid, last_mid, first_created, last_end);
  END IF;

  CLOSE cur;
END
//

DROP TABLE IF EXISTS results //
CREATE TEMPORARY TABLE results AS SELECT userid, mid, created, created as ends FROM transactions WHERE 0 //
call get_memberships //
SELECT * FROM results //
DROP TABLE results;

但是,这种技术的一个缺点是使用临时表。

在具有“想要的输出”的表中,是否有意对最后4条记录使用id 2?这不应该是独一无二的吗?2014-10-17=>2014-10-31为14天,因此我计算2014-10-17交易为44天,因此下一笔交易为59天。我做错什么了吗?@lp_uuuMyBad它应该是唯一的id,是的,它应该是44天,由30天计算,而不是31天。谢谢你指出,修好了。也将尝试您的解决方案。在具有“想要的输出”的表中,您是否有意在最后4条记录中使用id 2?这不应该是独一无二的吗?2014-10-17=>2014-10-31为14天,因此我计算2014-10-17交易为44天,因此下一笔交易为59天。我做错什么了吗?@lp_uuuMyBad它应该是唯一的id,是的,它应该是44天,由30天计算,而不是31天。谢谢你指出,修好了。我也会尝试你的解决方案。这看起来很有希望,不知道用户定义的变量,或者虽然mysql有,但我不是经常使用mysql,只是为了控制用户和内存。脚本几乎达到了我想要的效果,但计算是不正确的,无法理解哪里出了问题。只有一件事需要澄清。一个新成员可以扩展相同类型的成员资格,或者这并不重要?e、 g.如果我的支持者会员资格将于12:31到期。我今天买了试用版,我的会员资格会持续到1月31日吗?或者这是两件不同的事情,我仍然有我的支持者ms直到12.31和测试访问直到01.18?因为上面的查询将把这两个不同的东西合并在一起。这可能是查询失败的原因。我更新了处理不同成员身份的答案,并尝试在php中实现相同的想法是一样的。。。。即使它不是完美的,我也希望它能帮助你找到一个可行的解决方案。这看起来很有希望,不知道用户定义的变量,或者虽然mysql有,但我不是经常使用mysql,只是为了控制用户和内存。脚本几乎达到了我想要的效果,但计算是不正确的,我无法理解哪里出了问题,只有一件事需要澄清。一个新成员可以扩展相同类型的成员资格,或者这并不重要?e、 g.如果我的支持者会员资格将于12:31到期。我今天买了试用版,我的会员资格会持续到1月31日吗?或者这是两件不同的事情,我仍然有我的支持者ms直到12.31和测试访问直到01.18?因为上面的查询将把这两个不同的东西合并在一起。这可能是查询失败的原因。我更新了处理不同成员身份的答案,并尝试在php中实现相同的想法是一样的。。。。即使它并不完美,我希望它能帮助你找到一个可行的解决方案。