我正在使用JPA 2.1和SQL Server数据库。以下是表格(语法适用于PostgreSQL,因为目前最容易为我编写):
CREATE TABLE users
(
id serial primary key,
locked boolean,
name varchar(20)
);
CREATE TABLE subscriptions
(
id serial,
name varchar(20),
id_user integer references users
);
我想选择没有特定名称订阅的所有活动(未锁定)用户。用户可能没有订阅或者也可能有多个订阅。
我目前的JPQL查询的SQL版本是
SELECT *
FROM users
WHERE locked = false
AND id NOT IN (SELECT id_user
FROM subscriptions
WHERE name = 'premium')
我在某处读过SQL Server将为外部SELECT的每个结果行执行嵌套的SELECT。即使嵌套SELECT的结果集不会像在这种情况下执行查询那样改变,这是真的吗?
随着表的增长,此查询具有可怕的运行时性能。如何在SQL Server中重写或调整此查询?也许使用JOIN?
答案 0 :(得分:1)
您可以通过将查询转换为not exists()
来获得小:
select *
from dbo.users u
where locked = 0
and not exists (
select 1
from dbo.subscriptions s
where s.id_user = u.id
and s.name = 'premium'
)
您还可以通过subscriptions
上的适当支持索引来提高效果,例如:
create nonclustered index ix_subscriptions_id_user_name
on dbo.subscriptions (id_user)
include (name);
您可以更进一步,使其成为过滤索引,但这可能不会显着提升性能。
答案 1 :(得分:0)
假设您已在连接列上创建索引,请尝试:
SELECT *
FROM users AS A
LEFT JOIN subscriptions as B
ON B.id_user = A.id
WHERE A.locked = 'false' AND B.name != 'premium' AND B.id_user IS NULL
答案 2 :(得分:0)
已经在某处读过SQL Server将为外部SELECT
的每个结果行执行嵌套的SELECT
根本不是真的。
这个查询:
'\h'
会为所有未锁定且没有非高级订阅的用户提供。
要获得所有未获得高级订阅的非锁定用户,请执行以下操作:
SELECT *
FROM users
WHERE locked = false
AND id NOT IN (SELECT id_user
FROM subscriptions
WHERE name != 'premium')
要在SQL Server中测试这样的东西,得到SQL Server Management Studio(或类似的),并运行如下脚本:
SELECT *
FROM users u
WHERE locked = 0
AND NOT EXISTS (SELECT *
FROM subscriptions
where id_user = u.id
and name = 'premium')
答案 3 :(得分:0)
更好地使用LEFT JOIN
,当然如果在连接条件上创建索引,查询会变得更快。
SELECT u.*
FROM users u
LEFT JOIN subscriptions s
ON s.id_user = u.id
WHERE u.locked = 'false'
AND s.name != 'premium'
AND s.id_user IS NULL
答案 4 :(得分:0)
首先在订阅上创建聚簇索引。如果表非常大,则在堆上维护索引可能是一场噩梦。我可能会建议在id_User上进行聚类。
CREATE TABLE subscriptions
(
id INT IDENTITY(1,1) PRIMARY KEY NONCLUSTERED,
name VARCHAR(20),
id_user INT
);
CREATE CLUSTERED INDEX CL_id_User ON Subscriptions (id_User)
CREATE TABLE users
(
id INT IDENTITY(1,1),
locked BIT,
name varchar(20),
CONSTRAINT PK_users PRIMARY KEY CLUSTERED (id)
);
然后在订阅和用户上创建非聚集索引。如果您想要从两个表中拉回所有列,则使用包含。
CREATE NONCLUSTERED INDEX IX_Users_Locked ON Users (Locked) INCLUDE (Name);
CREATE NONCLUSTERED INDEX IX_Subscriptions_Name ON Subscriptions (name);
然后让您的查询看起来像下面的内容 -
SELECT *
FROM users u
WHERE u.id NOT IN (SELECT s.id_user
FROM Subscriptions s
WHERE s.name = 'Premium')
AND u.locked = 0;
更进一步,而是使用ID来确定订阅类型。索引整数远远优于SQL Server中的索引字符串。