如果另一个值不存在,如何根据一个值从表中选择项目? (口才/ SQL)

时间:2016-09-10 07:41:22

标签: mysql sql eloquent

我有一张桌子,可以为我的网站存储不同的文本块。我目前正在为页面的slug和首选语言为每个页面选择正确的块。当首选语言不可用时,我想在后备语言中选择相同的文本块(具有相同的标题)。

阻止表

columns:
| id | slug | title | language | content |

entries:
| 1 | home | first | en | the first block |
| 2 | home | first | nl | het eerste blok |
| 3 | home | second | en | the second block |
    --> block "second" not available for the 'nl' language

必需输出

fallback language = en

selected prefered language = en, output:
| 1 | home | first | en | the first block |
| 3 | home | second | en | the second block |

selected prefered language = nl, output:
| 2 | home | first | nl | het eerste blok |
| 3 | home | second | en | the second block |
     --> select this one, because the 'nl' version is not available

目前我只选择具有所选首选语言的块,因为如果“首选语言”块不可用,我不知道如何通过选择“后备语言”块来解决这个问题。我可以尝试为这两种语言运行两个查询,然后以某种方式合并它们,如果此块的标题计数低于1,则只插入“后备语言”块,但这看起来相当复杂而且不是很优雅?

对于我的应用程序,我使用雄辩:

$blocks = Block::->where('slug', '=', 'home')
                ->whereIn('language', $selectedLanguage)
                ->get();

我怎么能在雄辩中这样做? (或者在原始SQL中,就此而言?)

2 个答案:

答案 0 :(得分:2)

如果我理解正确,您希望使用后备语言“删除”行,如果已有一行包含相同slugtitle的首选语言。

您可以使用LEFT JOIN作为后备语言来检查是否存在具有首选语言的条目。例如,如果您喜欢的语言为'nl'且后备语言为'en',则您的查询可能如下所示:

select blocks.*
from blocks
left join blocks b1
    on  b1.slug  = blocks.slug
    and b1.title = blocks.title
    and b1.language = 'nl'
    and blocks.language <> 'nl'
where blocks.slug = 'home'
  and blocks.language in ('nl', 'en')
  and b1.id is null

sqlfiddle

单词中的联接可能类似​​于:为同一slugtitle寻找更好的翻译。如果语言是首选语言,则由于blocks.language <> 'nl'而不会匹配。否则,联接将“搜索”首选翻译('b1.language = 'nl')。

在WHERE子句中,如果找不到更好的翻译,我们只会返回行(b1.id is null)。

我能将查询转换为eloquent的最佳方法是:

$prefered = 'nl';
$fallback = 'en';

$blocks = App\Block::where('blocks.slug', '=', 'home')
    ->whereIn('blocks.language', [$prefered, $fallback])
    ->leftJoin('blocks as b1', function($join) {
        $join->on('b1.slug', '=', 'blocks.slug')
             ->on('b1.title', '=', 'blocks.title')
             ->on('b1.language', '=', DB::raw('?'))
             ->on('blocks.language', '<>', DB::raw('?'))
        ;
    })
    ->whereNull('b1.id')
    ->addBinding([$prefered, $prefered], 'join')
    ->select(DB::raw('blocks.*'))
    ->get()
;

注意:我假设title对于所有语言的块都是相同的。否则,您需要另一列(如block_id)来标识块。

答案 1 :(得分:1)

您可以使用GROUP_CONCAT来执行此操作。

  1. title分组,以获取一行中的所有相关字符串。
  2. 使用GROUP_CONCAT的{​​{1}}将所需语言放在首位。
  3. 使用ORDER BY仅提取第一个字符串。
  4. 示例查询:

    SUBSTRING_INDEX