使用带有INNER JOIN

时间:2016-05-29 04:28:29

标签: android sqlite inner-join

编辑:在原始问题后添加了解决方案。结果问题与临时表中的聚合函数有关,而不是与内部联接的参数标记有关。决议中有更详细的解释。

这个问题不重复。这个问题与IN子句的存在无关,我通过从查询中删除IN子句来说明缺陷行为是否与IN子句无关。

在下面的问题中,问题的确切位置似乎有些混乱。 IN子句中的问题是。问题是用pheno_temp1.pheno_count =?指定的参数?没有被替换。

为了解决这个问题的误读,我重写了两个rawQuery调用,以说明行为是在内连接的参数替换中。在以下调用中没有IN子句,它们表现出与原始两个调用(我保留以供参考)中指定的完全相同的行为。

// More restrictive example. This call gives the correct result;
// note there are NO parameter markers.
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=1",null);

// This call returns NO results. pheno_count=1 has been replaced with
// pheno_count=?
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=?",new String[]{"1"});

// This query still gives the correct result; It includes a parameter
// marker for a column from the organism table.
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=1 AND organism.bbch_stage=?",new String[]{"9"});

// This query still returns no rows. It includes 2 parameter markers;
// one for a column from organism (see above call which worked) and one
// for pheno_count from the inner table.
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=? AND organism.bbch_stage=?",new String[]{"1","9"});

我正在观察执行rawQuery时的行为,这表明在使用selectionArgs为内连接中指定的表传递where子句的值时会出现错误。

我在SQLite数据库中有以下表格:

table organism
org_id  bbch_stage  org_name                            (other data)
-----------------------------------------------------------------------
     0           9  homozygous dominant starter plant   (miscellaneous)
     1           9  homozygous recessive starter plant  (miscellaneous)

table pheno_temp1
org_id   pheno_count
--------------------
     0             1

以下调用返回不同的结果集:

// This call returns the correct result set
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=1 AND organism.bbch_stage IN (?)",new String[]{"9"});

// This call returns no rows
cursor = _mendelDatabase.rawQuery("SELECT organism.org_id FROM organism INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id WHERE pheno_temp1.pheno_count=? AND organism.bbch_stage IN (?)",new String[]{"1","9"});

这对我来说似乎是异常行为。但是,在我尝试提交错误报告之前,我想知道rawQuery是否存在一些微妙的行为,内部联接和selectionArgs在文档中并不明显。

我在运行Android 4.1.1的HTC One上观察到这种行为。开发环境是Android Studio 2.1.1。编译SDK 23,构建工具23.0.3,min SDK 14,目标SDK 23. Java 1.8.0_91。

背景:

上面的查询是最简单的查询,说明了应用中可能出现的问题。真实的查询可能会随意包括对母系和父系的有机体限制,有机体“出生”日期(两个日期之前,之后,之间或之间),或多个bbch阶段;多种表型的规格,或来自遗传表的多个等位基因的说明(在上面的例子中省略)。限制的任意性质使得观点不合适。相反,我为表型和等位基因限制(如果需要)构建工作表,然后在内部联接中使用它们来限制从有机体表中选择的行(如果需要,再次)。

通过构建临时表来设置对表型和等位基因的限制,然后临时表连接到生物体表。如果用户已对表型设置了限制,pheno_temp1表将指定(在pheno_count中)每个生物体满足多少这些限制。因此,如果用户设置了三个表型限制,则只有pheno_temp1pheno_count=3的条目才能满足用户的条件。在内连接中,pheno_count上的where子句必须包含不同的值。类似(但明显更复杂)的机制用于限制等位基因的结果。

我可以通过各种方式对rawQuery行为进行编码,而无需将计数值连接到where子句(涉及临时表的更多步骤),因此我不想寻找解决该问题的方法。 我正在寻找的是在我打开错误报告之前确认行为被窃听。

在本网站的其他地方讨论更一般的“我如何做内部联接”问题表明第二个查询(不返回任何行的那个)应该工作,但所有的答案我已经看过像“这是一个例子......”之类的话,而不是“这是我测试过的代码并且知道某些作品。” (...除了可能这个问题SQLite rawquery selectionArgs not working)。

1 个答案:

答案 0 :(得分:1)

解决

为了帮助那些在处理类似问题时遇到这个问题的人,这就是我的决心。每个失败的查询(返回0行)都有两个共同点。

  1. 所有查询都涉及一个内连接,其中连接表中的一列用于限制返回的行(通过inner_table.column = ?, selectionArgs用于提供替换值);
  2. 用于限制返回行的每个内部表列都是某种聚合函数(count,sum)的结果。
  3. 所有失败的查询都有一个内连接的事实使我不知道参数替换失败的所有列都是聚合函数。

    这些是用于定义和填充查询中涉及的表的SQL命令:

    CREATE TABLE IF NOT EXISTS `organism` (
      `org_id` INT NOT NULL,
      `org_name` INT NULL,
      `mat_id` INT NULL,
      `pat_id` INT NULL,
      `map_id` INT NOT NULL,
      `bbch_stage` INT NULL,
      -- other columns not relevant to problem
      PRIMARY KEY (`org_id`),
      -- constraints not relevant to problem
    );
    -- indexes not relevant to problem
    
    CREATE TABLE IF NOT EXISTS `gene_sequence` (
      `org_id` INT NOT NULL,
      `gene_id` INT NOT NULL,
      `allele_id` INT NOT NULL,
      -- constraints not relevant to problem
    );
    -- indexes not relevant to problem
    
    CREATE TABLE IF NOT EXISTS `organism_phenotype` (
      `org_id` INT NOT NULL,
      `map_id` INT NOT NULL,
      `phenogroup_id` INT NOT NULL,
      `phenotype_id` INT NOT NULL,
      -- constraints not relevant to problem
    );
    -- indexes not relevant to problem
    
    INSERT INTO `organism`
       (`org_id`, `org_name`, `mat_id`, `pat_id`,  `map_id`, `bbch_stage` /* other columns omitted for brevity */)
       VALUES (0, "homozygous dominant starter plant", null, null, 0, 9 /* other columns omitted for brevity */);
    -- Yes, gene_sequence contains duplicate records; this is valid for the
    -- problem domain space 
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 0, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 0, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 1, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 1, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 2, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 2, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 3, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 3, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 4, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 4, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 5, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 5, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 6, 0);
    INSERT INTO gene_sequence (`org_id`,`gene_id`,`allele_id`) VALUES (0, 6, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 0, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 1, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 2, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 3, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 4, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 5, 0);
    INSERT INTO `organism_phenotype` (`org_id`,`map_id`,`phenogroup_id`,`phenotype_id`) VALUES (0, 0, 6, 0);
    

    当从数据库中搜索生物时,用户可以指定生物体必须展示/拥有的一种或多种表型值或等位基因。通过基于指定条件构建临时表来应用这些限制。对于表型,生成的查询字符串如下所示:

    CREATE TEMP TABLE pheno_temp1 AS
      SELECT org_id,count(org_id) AS 'pheno_count'
        FROM organism_phenotype
        WHERE (phenogroup_id=? AND phenotype_id=?)
          -- the OR clause appears optionally for each phenotype specified
          -- beyond the first
          OR (phenogroup_id=? AND phenotype_id=?)
        GROUP BY org_id
    

    使用String []将字符串传递给rawQuery,并指定参数的替换值。

    因此,如果用户需要所有展示紫色花(phenogroup = 0,表型= 0)和轴向花位置(phenogroup = 1,表型= 0)的豌豆植物的列表,软件将定义查询字符串和selectionArgs为如下:

    String queryString =
      "CREATE TEMP TABLE pheno_temp1 AS" +
        " SELECT org_id,count(org_id) AS 'pheno_count'" +
          " FROM organism_phenotype" +
          " WHERE (phenogroup_id=? AND phenotype_id=?)" +
            " OR (phenogroup_id=? AND phenotype_id=?)" +
          " GROUP BY org_id";
    String[] selectionArgs = {"0","0","1","0"};
    

    这(大部分)按预期工作,并创建一个包含预期内容的表:

    table pheno_temp1
    org_id   pheno_count
    --------------------
         0             2
    

    过滤等位基因的过程类似,但涉及两个级别的聚合和DISTINCT操作(由于基本问题相同,我在这里省略了这些细节)。

    在构建临时表之后,该过程的下一步是通过将有机体连接到pheno_temp1和geno_temp2(来自等位基因过滤的最终工作表)来选择满足所有用户标准的org_id值。此步骤的查询通常如下所示:

    SELECT organism.org_id FROM organism
      INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id
      INNER JOIN geno_temp2 ON organism.org_id=geno_temp2.org_id
        WHERE
          -- the query will include one or more of these clauses depending
          -- on the user's selections; the software takes care of adding the
          -- appropriate clauses and necessary glue (AND) between clauses
          pheno_temp1.pheno_count=?
          AND geno_temp2.allele_sum=?
          -- the query will include at most one of the birth_date clauses
          AND organism.birth_date<?
          --AND organism.birth_date=?
          --AND organism.birth_date>?
          --AND organism.birth_date BETWEEN ? AND ?
          -- the bbch_stage clause includes one parameter marker for each
          -- bbch stage selected by the user
          AND organism.bbch_stage IN (?,?,?)
          -- maternal and paternal lineage
          AND organism.mat_id=?
          AND organism.pat_id=?
        ORDER BY organism.org_id
    

    如果用户请求了单个表型(因此phenocount = 1)和单个bbch阶段(9),则sql查询字符串和选择参数将定义如下:

    String queryString =
      "SELECT organism.org_id FROM organism" +
        " INNER JOIN pheno_temp1 ON organism.org_id=pheno_temp1.org_id" +
          " WHERE pheno_temp1.pheno_count=?" +
            " AND organism.bbch_stage IN (?)" +
          " ORDER BY organism.org_id";
    String[] selectionArgs = {"1","9"};
    

    SQL查询和选择参数都是正确的;有机体和pheno_temp1中的数据表明rawQuery调用应返回org_id = 0的单行(严格来说,结果集上的光标包含org_id = 0的有机体记录的单行,但是我要去从这一点开始假设每个读这篇文章的人都知道我的意思。)

    问题在于:查询不返回任何行。由于某种原因,CREATE TABLE AS SELECT查询中的聚合列不能与rawQuery()中的参数标记一起使用(和实际上我的测试显示它们不能在query()中使用。

    该解决方案最终改变了为表型和等位基因过滤构建临时表的方式。该表不是使用CREATE TABLE AS SELECT语句,而是在前面的步骤中显式创建的:

    _mendelDatabase.execSQL("CREATE TEMP TABLE pheno_temp1 ('org_id' INT NOT NULL,'pheno_count' INT NOT NULL");
    

    填充此表的查询字符串随后更改为:

    String queryString =
      "INSERT INTO pheno_temp1 (org_id,pheno_count)" +
        " SELECT org_id,count(org_id) AS 'pheno_count'" +
          " FROM organism_phenotype" +
          " WHERE (phenogroup_id=? AND phenotype_id=?)" +
            " OR (phenogroup_id=? AND phenotype_id=?)" +
          " GROUP BY org_id";
    

    (selectionArgs无需更改。)

    最后一个观察结果:关于参数标记和选择参数的许多(不是全部)问题暗示它们只能在基础表列是字符串类型时使用。这显然不是真的;上面的所有代码都直接从应用程序中提取,我发现了这个问题,参数替换使用的大多数列都是整数类型;我证明,如果对临时表的创建方式进行了更改,代码就可以正常工作。

    我想感谢CL的时间和精力。虽然CL没有提供问题的实际解决方案,但来回确实最终导致了指向答案方向的测试用例(并且公平地说CL原始问题没有说明内部连接列是来自聚合函数)。