加入并没有给出正确的结果

时间:2017-07-09 10:08:12

标签: mysql sql

我正在使用join运行以下查询。但是这个联接并没有给出正确的结果

SELECT pr.start_date,pr.end_date, p.contribution_id 
FROM civicrm_membershipperiod pr 
INNER JOIN civicrm_membership_payment p ON pr.membership_id=p.membership_id
where p.membership_id=11

两个表都包含三个记录

mysql> select *  FROM `civicrm_membershipperiod` ;
+----+---------------+------------+------------+
| id | membership_id | start_date | end_date   |
+----+---------------+------------+------------+
|  1 |            11 | 2015-01-01 | 2015-12-31 |
|  2 |            11 | 2017-07-09 | 2018-07-08 |
|  3 |            11 | 2018-07-09 | 2019-07-08 |
+----+---------------+------------+------------+
3 rows in set (0.00 sec)
--------------------------------------------------------------------------
mysql> select *  FROM `civicrm_membership_payment` ;
+----+---------------+-----------------+
| id | membership_id | contribution_id |
+----+---------------+-----------------+
| 27 |            11 |              39 |
| 28 |            11 |              40 |
| 29 |            11 |              41 |
+----+---------------+-----------------+
3 rows in set (0.00 sec)

但结果是九条记录而不是三条记录。

mysql> SELECT pr.start_date,pr.end_date, p.contribution_id FROM civicrm_membershipperiod pr INNER JOIN civicrm_membership_payment p ON pr.membership_id=p.membership_id where p.membership_id=11;
+------------+------------+-----------------+
| start_date | end_date   | contribution_id |
+------------+------------+-----------------+
| 2015-01-01 | 2015-12-31 |              39 |
| 2015-01-01 | 2015-12-31 |              40 |
| 2015-01-01 | 2015-12-31 |              41 |
| 2017-07-09 | 2018-07-08 |              39 |
| 2017-07-09 | 2018-07-08 |              40 |
| 2017-07-09 | 2018-07-08 |              41 |
| 2018-07-09 | 2019-07-08 |              39 |
| 2018-07-09 | 2019-07-08 |              40 |
| 2018-07-09 | 2019-07-08 |              41 |
+------------+------------+-----------------+
9 rows in set (0.00 sec)

我不确定错误在哪里。

这是我想要的:

2015-01-01  2015-12-31  39
2017-07-09  2018-07-08  40
2018-07-09  2019-07-08  41 

4 个答案:

答案 0 :(得分:0)

在这种情况下,我希望看到9条记录。这是因为第一个表中的membership_id为11的记录有三个,第二个表中的membership的三个记录为11个,3 * 3 = 9,即第一个表中membership_id为11的每一行都与membership_id的每一行连接第二个表中的11个。

这里的问题是这两个表之间存在多对多的关系,因此您需要一个联结。但是,你真的需要:civicrm_membership_payment表吗?你能将contrib_id从civicrm_membership_payment移到civicrm_membershipperiod,然后删除civicrm_membership_payment吗?然后就做:

select * from civicrm_membershipperiod

如果你不能这样做,那么我会查看Junction Tables中的多对多关系。

答案 1 :(得分:0)

根据您所写的内容,结果绝对正确。 两个表civicrm_membershipperiod和civicrm_membership_payment中每个元组的membership_id为11。 由于civicrm_membershipperiod的每个元组与列membership_id上的另一个表的每个元组匹配,因此它找不到不匹配。因此,它产生3 * 3 = 9次匹配。

答案 2 :(得分:0)

根据您的数据,这是预期的行为。您使用JOIN membership_id了两张桌子。但是,civicrm_membership_paymentmembership_id = 11的每一行与另一个表中的三个记录匹配。这就是你的结果中有3 x 3 = 9行的原因。

这看起来像是设计缺陷,您的表civicrm_membership_payment应该有一个参考,允许它决定此付款所涉及的时间段

dbfiddle here 了解您当前的情况。

备用:更改表格的定义以说明:

CREATE TABLE civicrm_membershipperiod
(
    /* no need for a synthetic id, surrogate primary key */
    membership_id INTEGER,
    start_date date,
    end_date date,
    PRIMARY KEY (membership_id, start_date),  /* "Natural" PK */
    CHECK (end_date >= start_date)
) ;

INSERT INTO civicrm_membershipperiod
    (membership_id, start_date, end_date)
VALUES
    (11, '2015-01-01', '2015-12-31'),
    (11, '2017-07-09', '2018-07-08'),
    (11, '2018-07-09', '2019-07-08') ; 

CREATE TABLE civicrm_membership_payment
(
    contribution_id INTEGER PRIMARY KEY,  /* Surrogate key, not really necessary */
    membership_id   INTEGER NOT NULL,
    start_date      date NOT NULL,

    CONSTRAINT fk_civicrm_membershipperiod_civicrm_membershipperiod 
        FOREIGN KEY (membership_id, start_date)
        REFERENCES civicrm_membershipperiod(membership_id, start_date)
) ;

INSERT INTO civicrm_membership_payment
    (membership_id, contribution_id, start_date)
VALUES
    (11, 39, '2015-01-01'),
    (11, 40, '2017-07-09'),
    (11, 41, '2018-07-09') ;

SELECT 
    pr.start_date,pr.end_date, p.contribution_id 
FROM 
    civicrm_membershipperiod pr 
    INNER JOIN civicrm_membership_payment p 
        ON p.membership_id = pr.membership_id AND p.start_date = pr.start_date
WHERE 
    p.membership_id=11
start_date | end_date   | contribution_id
:--------- | :--------- | --------------:
2015-01-01 | 2015-12-31 |              39
2017-07-09 | 2018-07-08 |              40
2018-07-09 | 2019-07-08 |              41

dbfiddle here

注意:您不需要每个表都有AUTO_INCREMENT个ID。它们在某些情况下可能很方便,但您也可以使用自然主键(列或列的组合,可以让您唯一地标识表中的行,并且具有一些含义< / em>的)。我已经使用它们而不是合成PK,我认为它们可以更好地代表手头的问题。

答案 3 :(得分:0)

正如我在评论中提到的,默认情况下,SQL不考虑数据库中行的顺序,因此它不能将一个表的第一行与另一个表的第一行匹配,依此类推。

几十年来,人们已经接受了这一点,但最近sql扩展了解决此问题的新语句。不幸的是,这还没有标准化,因此导致了SQL方言的进一步分歧。对于T-SQL,请参阅row_number()rank()

上的此文档

溶剂是在加入之前首先添加行号:

SELECT pr.start_date,pr.end_date, p.contribution_id 
FROM (select *, ROW_NUMBER() 
                OVER(PARTITION BY membership_id 
                ORDER BY start_date ASC) as row_nr
      from civicrm_membershipperiod ) pr 
INNER JOIN (select *, ROW_NUMBER() 
                OVER(PARTITION BY membership_id 
                ORDER BY contribution_id ASC) as row_nr
      from civicrm_membership_payment ) p 
ON pr.membership_id=p.membership_id and pr.row_nr = p.row_nr
where p.membership_id=11