获取具有相同列中不同值并具有另一列条件的记录。

huangapple go评论68阅读模式
英文:

How to get records with different values in same column AND condition on another column

问题

我们正在尝试通过SQL查询从Moodle数据库中获取经过筛选的自定义字段数据。不幸的是,由于Moodle以这种方式存储其数据,我们需要连接多个表来获取所需的数据。而且,我们无法操纵数据结构。数据库看起来类似于这样(简化的版本):

// 表 mdl_customfield_field
ID shortname

8 language
11 institutions

// 表 mdl_customfield_data
ID fieldid instanceid value

1 8 1 2
2 8 2 1
3 11 1 1,2
4 11 2 2,3
5 11 3 1

我们尝试使用以下代码获取满足语言为1且机构为2的每个记录。

SELECT * FROM mdl_course c
JOIN mdl_customfield_data d ON c.id = d.instanceid
JOIN mdl_customfield_field f ON f.id = d.fieldid
WHERE (f.shortname = 'institutions' AND (d.value LIKE ',2,' OR d.value LIKE '2,'
OR d.value LIKE ',2' OR d.value = '2'))
AND (f.shortname = 'language' AND d.value = '1')

在查询的最后一行使用AND时,结果集不包含语言数据,并且使用OR会显示太多结果。如何只获取满足两个条件的课程?

英文:

We are trying to get filtered custom field data out of the Moodle database via SQL query. Unfortunately it's necessary to join multiple tables to get the data we need, because Moodle keeps its data this way. Also we can't manipulate the data structure. The database looks something like this (simplified):

// table mdl_customfield_field
ID  shortname
================
8   language
11  institutions

// table mdl_customfield_data
ID  fieldid instanceid  value
=============================
1   8       1           2
2   8       2           1
3   11      1           1,2
4   11      2           2,3
5   11      3           1

We tried to get every record that has language 1 AND institution 2 with the following code.

SELECT * FROM mdl_course c
JOIN mdl_customfield_data d ON c.id = d.instanceid
JOIN mdl_customfield_field f ON f.id = d.fieldid
WHERE (f.shortname = 'institutions' AND (d.value LIKE '%,2,%' OR d.value LIKE '2,%' 
OR d.value LIKE '%,2' OR d.value = '2'))
AND (f.shortname = 'language' AND d.value = '1')

Using an AND in the last line of the query, the result set does not contain the language data and an OR is showing too many results. How can we just get the course that satisfies both conditions in the result?

答案1

得分: 1

这似乎是一个关系除法问题。一种选项是使用关联子查询来筛选具有两个字段都可用且设置为期望值的课程记录:

select c.*
from mdl_course c
where (
    select count(*)
    from mdl_customfield_data d
    inner join mdl_customfield_field f on f.id = d.fieldid
    where d.instance_id = c.id
      and (
           ( f.shortname = 'institutions' and '2' = any string_to_array(d.value, ',') )
        or ( f.shortname = 'language'     and '1' = any string_to_array(d.value, ',') )
) = 2

您可以轻松扩展查询以适应更多的条件,通过在子查询中添加更多的 or 谓词,并相应地递增匹配的期望计数(目前为 2)。

请注意,在数据库列中以CSV格式存储值列表(在此处为mdl_customfield_field表的value列)不是良好的做法;应该有一个单独的表来存储这种1-N关系。

英文:

> How would we get every record that has language 1 AND institution 2?

This reads like a relational division problem. One option uses a correlated subquery that filters courses that have both fields available and set to the expected value:

select c.*
from mdl_course c
where (
    select count(*)
    from mdl_customfield_data d
    inner join mdl_customfield_field f on f.id = d.fieldid
    where d.instance_id = c.id
      and (
           ( f.shortname = 'institutions' and '2' = any string_to_array(d.value, ',') )
        or ( f.shortname = 'language'     and '1' = any string_to_array(d.value, ',') )
) = 2

You could easily expand the query to accommodate for more criteria, by adding more or predicates in the subquery and incrementing the expected count of matches accordingly (currently 2).

Note that storing a list of values in CSV format in a database column (as here in column value ot table mdl_customfield_field) is not good practice; there should be a separate table to store this 1-N relationship.

答案2

得分: 1

也许将学院和语言视为两个表格

SELECT c.*
FROM mdl_course c
JOIN (
    SELECT d.instanceid AS courseid
    FROM mdl_customfield_data d
    JOIN mdl_customfield_field f ON f.id = d.fieldid AND f.shortname = 'language'
    WHERE d.value = '1'
) l ON l.courseid = c.id
JOIN (
    SELECT d.instanceid AS courseid
    FROM mdl_customfield_data d
    JOIN mdl_customfield_field f ON f.id = d.fieldid AND f.shortname = 'institutions'
    WHERE d.value ~ '\y2\y'
) i ON i.courseid = c.id

d.value ~ '\y2\y' 中的波浪号用于在SQL中匹配正则表达式。

\y 用于在PostgreSQL中表示单词边界,其他数据库使用 \b 作为单词边界。这将搜索整个单词或数字,因此 2 将匹配 2,但不会匹配 28 或 82。在字符串字段中搜索逗号分隔的值时非常有用。

您还可以使用 d.value ~ '[[:<:]]2[[:>:]]'

如果您正在Moodle中开发此代码,那么您可以使用Moodle的SQL兼容性函数使SQL代码更通用。

$regexp = $DB->sql_regex(true);
$regexwordbegin = $DB->sql_regex_get_word_beginning_boundary_marker();
$regexwordend = $DB->sql_regex_get_word_end_boundary_marker();

$where = "d.value {$regexp} '{$regexwordbegin}:institute{$regexwordend}'";

:institute 作为 Moodle 中的参数传递。

英文:

Maybe treat the institute and language as 2 tables

SELECT c.*
FROM mdl_course c
JOIN (
    SELECT d.instanceid AS courseid
    FROM mdl_customfield_data d
    JOIN mdl_customfield_field f ON f.id = d.fieldid AND f.shortname = &#39;language&#39;
    WHERE d.value = &#39;1&#39;
) l ON l.courseid = c.id
JOIN (
    SELECT d.instanceid AS courseid
    FROM mdl_customfield_data d
    JOIN mdl_customfield_field f ON f.id = d.fieldid AND f.shortname = &#39;institutions&#39;
    WHERE d.value ~ &#39;\y2\y&#39;
) l ON i.courseid = c.id

d.value ~ &#39;\y2\y&#39; the tilde is for matching regular expressions in SQL

The \y is used for word boundaries in postgresql, other databases use \b as the word boundary. This will search for a whole word or number, so 2 will match 2 but not 28 or 82. Useful for searching comma separated values in string fields.

You could also use d.value ~ &#39;[[:&lt;:]]2[[:&gt;:]]&#39;

If you are developing this in Moodle, then you could use Moodle's sql compatibility functions to make the SQL code generic

$regexp = $DB-&gt;sql_regex(true);
$regexwordbegin = $DB-&gt;sql_regex_get_word_beginning_boundary_marker();
$regexwordend = $DB-&gt;sql_regex_get_word_end_boundary_marker();

$where = &quot;d.value {$regexp} &#39;{$regexwordbegin}:institute{$regexwordend}&#39;`

:institute pass as a parameter in Moodle

huangapple
  • 本文由 发表于 2023年5月26日 16:29:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76339029.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定