如何 select 包含特定单词的 postgreSQL 行
How to select a postgreSQL row that contains a specific word
正在尝试基于关键字为 postgreSQl 数据库构建查询。 LIKE 不起作用,因为它匹配包含任何字母的任何行。例如:
SELECT * 来自 table WHERE 列 ilike '%jeep%';
这 returns 列中包含 j、e 或 p 的任何行(由于某种原因同一行多次出现)。不是 'jeep'.
这个词
下面是我的查询结构。使用 Knex 并排队多个 tables:
searchAllBoardPosts(db, term) {
return db
.select('*')
.from({
a: 'messageboard_posts',
b: 'rentals',
c: 'market_place',
d: 'jobs'
})
.where('a.title', 'ilike', `%${term}%`)
.orWhere('b.title', 'ilike', `%${term}%`)
.orWhere('c.title', 'ilike', `%${term}%`)
.orWhere('d.title', 'ilike', `%${term}%`);
},
提前致谢!
更新:
这是 SQL 输出:
select *
from "messageboard_posts" as "a",
"rentals" as "b",
"market_place" as "c",
"jobs" as "d"
where "a"."title" ilike '%jeep%'
or "b"."title" ilike '%jeep%'
or "c"."title" ilike '%jeep%'
or "d"."title" ilike '%jeep%'
这个表达式:
WHERE column ilike 'jeep'
只匹配值为lower(column) = 'jeep'
的行,例如:
- 吉普
- 吉普
- 吉普车
它不匹配任何其他表达式。
如果使用通配符:
WHERE column ilike '%jeep%'
然后在 lower(column)
中的任意位置查找 'jeep'
。它是不是逐字符搜索。为此,您将使用正则表达式和字符 类:
WHERE column ~* '[jep]'
如果要在字段中查找词,通常使用正则表达式,而不是like
/ilike
。
使用 .union
有效并且 returns 正确的值,但是使用查询中第一个 table 的键标记。最后只进行了四个单独的查询,但希望这可以帮助其他人!
searchAllBoardPosts(db, term) {
return db
.union([db
.select('id', 'market_place_cat')
.from('market_place')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'board_id')
.from('messageboard_posts')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'rental_cat')
.from('rentals')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'job_cat')
.from('jobs')
.where('title', 'ilike', `%${term}%`)
]);
},
这个查询是一个交叉连接
(但是 Knex 语法稍微掩盖了这一点)。
This returns any row that a j,e or p in the column (and the same row multiple times for some reason).
这不是 return多次访问同一行。它 return 从 CROSS JOIN
中命名的每个 table 中提取所有内容。这是 Postgres 在 FROM
子句中指定多个 table 时的行为(参见:docs)。这个:
db
.select('*')
.from({
a: 'table_one',
b: 'table_two'
})
将 return 整行 来自 每个 的 table 每次你得到一个ILIKE
匹配。所以至少你总是会得到一个包含两行连接的对象(或者无论你在 FROM
子句中命名多少)。
棘手的部分是,Knex 列名称必须映射到 JavaScript 个对象。这意味着如果有两个名为 id
或 title
的列结果,最后一个将覆盖结果对象中的第一个。
让我们举例说明(用袋熊)
这里有一个迁移和种子,只是为了更清楚:
table_one
exports.up = knex =>
knex.schema.createTable("table_one", t => {
t.increments("id");
t.string("title");
});
exports.down = knex => knex.schema.dropTable("table_one");
table_two
exports.up = knex =>
knex.schema.createTable("table_two", t => {
t.increments("id");
t.string("title");
});
exports.down = knex => knex.schema.dropTable("table_two");
种子
exports.seed = knex =>
knex("table_one")
.del()
.then(() => knex("table_two").del())
.then(() =>
knex("table_one").insert([
{ title: "WILLMATCHwombatblahblahblah" },
{ title: "WILLMATCHWOMBAT" }
])
)
.then(() =>
knex("table_two").insert([
{ title: "NEVERMATCHwwwwwww" },
{ title: "wombatWILLMATCH" }
])
)
);
查询
这让我们可以尝试使用 ILIKE
匹配。现在我们需要使列名真正明确:
return db
.select([
"a.id as a.id",
"a.title as a.title",
"b.id as b.id",
"b.title as b.title"
])
.from({
a: "table_one",
b: "table_two"
})
.where("a.title", "ilike", `%${term}%`)
.orWhere("b.title", "ilike", `%${term}%`);
这会产生:
[
{
'a.id': 1,
'a.title': 'WILLMATCHwombatblahblahblah',
'b.id': 1,
'b.title': 'NEVERMATCHwwwwwww'
},
{
'a.id': 1,
'a.title': 'WILLMATCHwombatblahblahblah',
'b.id': 2,
'b.title': 'wombatWILLMATCH'
},
{
'a.id': 2,
'a.title': 'WILLMATCHWOMBAT',
'b.id': 1,
'b.title': 'NEVERMATCHwwwwwww'
},
{
'a.id': 2,
'a.title': 'WILLMATCHWOMBAT',
'b.id': 2,
'b.title': 'wombatWILLMATCH'
}
]
如您所见,它交叉连接了两个 table,但我怀疑您只看到 出现 不匹配的结果(因为匹配是在另一个 table 中,title
列名称重复)。
那么,查询应该是什么?
我认为您(或 Ry)使用 UNION
的计划是正确的,但可能值得使用 UNION ALL
以避免不必要地删除重复项。像这样:
return db
.unionAll([
db("market_place")
.select(db.raw("*, 'marketplace' as type"))
.where("title", "ilike", `%${term}%`),
db("messageboard_posts")
.select(db.raw("*, 'post' as type"))
.where("title", "ilike", `%${term}%`),
db("rentals")
.select(db.raw("*, 'rental' as type"))
.where("title", "ilike", `%${term}%`),
db("jobs")
.select(db.raw("*, 'job' as type"))
.where("title", "ilike", `%${term}%`)
]);
针对我们的测试数据的类似查询产生结果集:
[
{ id: 1, title: 'WILLMATCHwombatblahblahblah', type: 'table_one' },
{ id: 2, title: 'WILLMATCHWOMBAT', type: 'table_one' },
{ id: 2, title: 'wombatWILLMATCH', type: 'table_two' }
]
正在尝试基于关键字为 postgreSQl 数据库构建查询。 LIKE 不起作用,因为它匹配包含任何字母的任何行。例如:
SELECT * 来自 table WHERE 列 ilike '%jeep%';
这 returns 列中包含 j、e 或 p 的任何行(由于某种原因同一行多次出现)。不是 'jeep'.
这个词下面是我的查询结构。使用 Knex 并排队多个 tables:
searchAllBoardPosts(db, term) {
return db
.select('*')
.from({
a: 'messageboard_posts',
b: 'rentals',
c: 'market_place',
d: 'jobs'
})
.where('a.title', 'ilike', `%${term}%`)
.orWhere('b.title', 'ilike', `%${term}%`)
.orWhere('c.title', 'ilike', `%${term}%`)
.orWhere('d.title', 'ilike', `%${term}%`);
},
提前致谢!
更新: 这是 SQL 输出:
select *
from "messageboard_posts" as "a",
"rentals" as "b",
"market_place" as "c",
"jobs" as "d"
where "a"."title" ilike '%jeep%'
or "b"."title" ilike '%jeep%'
or "c"."title" ilike '%jeep%'
or "d"."title" ilike '%jeep%'
这个表达式:
WHERE column ilike 'jeep'
只匹配值为lower(column) = 'jeep'
的行,例如:
- 吉普
- 吉普
- 吉普车
它不匹配任何其他表达式。
如果使用通配符:
WHERE column ilike '%jeep%'
然后在 lower(column)
中的任意位置查找 'jeep'
。它是不是逐字符搜索。为此,您将使用正则表达式和字符 类:
WHERE column ~* '[jep]'
如果要在字段中查找词,通常使用正则表达式,而不是like
/ilike
。
使用 .union
有效并且 returns 正确的值,但是使用查询中第一个 table 的键标记。最后只进行了四个单独的查询,但希望这可以帮助其他人!
searchAllBoardPosts(db, term) {
return db
.union([db
.select('id', 'market_place_cat')
.from('market_place')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'board_id')
.from('messageboard_posts')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'rental_cat')
.from('rentals')
.where('title', 'ilike', `%${term}%`)
])
.union([db
.select('id', 'job_cat')
.from('jobs')
.where('title', 'ilike', `%${term}%`)
]);
},
这个查询是一个交叉连接
(但是 Knex 语法稍微掩盖了这一点)。
This returns any row that a j,e or p in the column (and the same row multiple times for some reason).
这不是 return多次访问同一行。它 return 从 CROSS JOIN
中命名的每个 table 中提取所有内容。这是 Postgres 在 FROM
子句中指定多个 table 时的行为(参见:docs)。这个:
db
.select('*')
.from({
a: 'table_one',
b: 'table_two'
})
将 return 整行 来自 每个 的 table 每次你得到一个ILIKE
匹配。所以至少你总是会得到一个包含两行连接的对象(或者无论你在 FROM
子句中命名多少)。
棘手的部分是,Knex 列名称必须映射到 JavaScript 个对象。这意味着如果有两个名为 id
或 title
的列结果,最后一个将覆盖结果对象中的第一个。
让我们举例说明(用袋熊)
这里有一个迁移和种子,只是为了更清楚:
table_one
exports.up = knex =>
knex.schema.createTable("table_one", t => {
t.increments("id");
t.string("title");
});
exports.down = knex => knex.schema.dropTable("table_one");
table_two
exports.up = knex =>
knex.schema.createTable("table_two", t => {
t.increments("id");
t.string("title");
});
exports.down = knex => knex.schema.dropTable("table_two");
种子
exports.seed = knex =>
knex("table_one")
.del()
.then(() => knex("table_two").del())
.then(() =>
knex("table_one").insert([
{ title: "WILLMATCHwombatblahblahblah" },
{ title: "WILLMATCHWOMBAT" }
])
)
.then(() =>
knex("table_two").insert([
{ title: "NEVERMATCHwwwwwww" },
{ title: "wombatWILLMATCH" }
])
)
);
查询
这让我们可以尝试使用 ILIKE
匹配。现在我们需要使列名真正明确:
return db
.select([
"a.id as a.id",
"a.title as a.title",
"b.id as b.id",
"b.title as b.title"
])
.from({
a: "table_one",
b: "table_two"
})
.where("a.title", "ilike", `%${term}%`)
.orWhere("b.title", "ilike", `%${term}%`);
这会产生:
[
{
'a.id': 1,
'a.title': 'WILLMATCHwombatblahblahblah',
'b.id': 1,
'b.title': 'NEVERMATCHwwwwwww'
},
{
'a.id': 1,
'a.title': 'WILLMATCHwombatblahblahblah',
'b.id': 2,
'b.title': 'wombatWILLMATCH'
},
{
'a.id': 2,
'a.title': 'WILLMATCHWOMBAT',
'b.id': 1,
'b.title': 'NEVERMATCHwwwwwww'
},
{
'a.id': 2,
'a.title': 'WILLMATCHWOMBAT',
'b.id': 2,
'b.title': 'wombatWILLMATCH'
}
]
如您所见,它交叉连接了两个 table,但我怀疑您只看到 出现 不匹配的结果(因为匹配是在另一个 table 中,title
列名称重复)。
那么,查询应该是什么?
我认为您(或 Ry)使用 UNION
的计划是正确的,但可能值得使用 UNION ALL
以避免不必要地删除重复项。像这样:
return db
.unionAll([
db("market_place")
.select(db.raw("*, 'marketplace' as type"))
.where("title", "ilike", `%${term}%`),
db("messageboard_posts")
.select(db.raw("*, 'post' as type"))
.where("title", "ilike", `%${term}%`),
db("rentals")
.select(db.raw("*, 'rental' as type"))
.where("title", "ilike", `%${term}%`),
db("jobs")
.select(db.raw("*, 'job' as type"))
.where("title", "ilike", `%${term}%`)
]);
针对我们的测试数据的类似查询产生结果集:
[
{ id: 1, title: 'WILLMATCHwombatblahblahblah', type: 'table_one' },
{ id: 2, title: 'WILLMATCHWOMBAT', type: 'table_one' },
{ id: 2, title: 'wombatWILLMATCH', type: 'table_two' }
]