慢子 SELECT GROUP BY JPQL 查询
Slow sub-SELECT GROUP BY JPQL query
以下 JPQL 针对 PG 9.6 执行大约需要 1.5 秒:
@Query(" SELECT t FROM T t WHERE t.id in ("
+ " SELECT MAX(t.id) FROM T t, C c "
+ " WHERE t.m = :m "
+ " AND t.c = c "
+ " AND c.createdDate < :date "
+ " GROUP BY t.m, t.p "
+ " ) ")
List<T> tByDate(@Param("m") M m, @Param("date") LocalDateTime date);
有什么想法可以在不更改数据库模型的情况下加快速度吗?
Table T(尺寸 45k)
CREATE TABLE public.t
(
id bigint NOT NULL DEFAULT nextval('t_id_seq'::regclass),
c_id bigint NOT NULL,
m_id bigint NOT NULL,
p_id bigint NOT NULL,
CONSTRAINT t_pkey PRIMARY KEY (id),
CONSTRAINT fkcdo362oanw5jshu29kavksyfy FOREIGN KEY (m_id)
REFERENCES public.m (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkjpyqqd0vys4jayau98eij2xv3 FOREIGN KEY (c_id)
REFERENCES public.c (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fknjjuiq1kn44mu5299dn67t3np FOREIGN KEY (p_id)
REFERENCES public.p (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table C(大小 45k)
CREATE TABLE public.c
(
id bigint NOT NULL DEFAULT nextval('c_id_seq'::regclass),
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
m_id bigint,
CONSTRAINT c_pkey PRIMARY KEY (id),
CONSTRAINT fkbv33l9w17owvi5kgctqvaepn0 FOREIGN KEY (m_id)
REFERENCES public.m (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table M(尺码 200)
CREATE TABLE public.m
(
id bigint NOT NULL DEFAULT nextval('m_id_seq'::regclass),
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
a_id bigint,
CONSTRAINT m_pkey PRIMARY KEY (id),
CONSTRAINT fkikqmae593j3mruwqy84pc56is FOREIGN KEY (a_id)
REFERENCES public.a (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table P(尺寸 5k)
CREATE TABLE public.p
(
id bigint NOT NULL,
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
e character varying(255) NOT NULL,
a_id bigint,
CONSTRAINT p_pkey PRIMARY KEY (id),
CONSTRAINT fkehggtafv310ewdtcq772pwl01 FOREIGN KEY (a_id)
REFERENCES public.a (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
This is a special case of the common "top N per category" problem,在SQL中有一些解决方案。为什么不直接使用原生 SQL?您的查询可以转换为:
SELECT (t).*
FROM (
SELECT t, ROW_NUMBER() OVER (PARTITION BY t.m_id, t.p_id ORDER BY t.id DESC) rn
FROM t
JOIN c ON t.c_id = c.id
WHERE t.m_id = :m
AND c.created_date < :date
) t
WHERE t.rn = 1
主要区别在于您可以避免一次访问 T
table。
当然,我假设您拥有所有适当的索引,包括所有外键和 C.CREATED_DATE
列的索引。结果仍然可以映射到实体。
请注意,I'm using PostgreSQL specific syntax to nest records,这样再次从投影中删除 RN
列就容易多了。
以下 JPQL 针对 PG 9.6 执行大约需要 1.5 秒:
@Query(" SELECT t FROM T t WHERE t.id in ("
+ " SELECT MAX(t.id) FROM T t, C c "
+ " WHERE t.m = :m "
+ " AND t.c = c "
+ " AND c.createdDate < :date "
+ " GROUP BY t.m, t.p "
+ " ) ")
List<T> tByDate(@Param("m") M m, @Param("date") LocalDateTime date);
有什么想法可以在不更改数据库模型的情况下加快速度吗?
Table T(尺寸 45k)
CREATE TABLE public.t
(
id bigint NOT NULL DEFAULT nextval('t_id_seq'::regclass),
c_id bigint NOT NULL,
m_id bigint NOT NULL,
p_id bigint NOT NULL,
CONSTRAINT t_pkey PRIMARY KEY (id),
CONSTRAINT fkcdo362oanw5jshu29kavksyfy FOREIGN KEY (m_id)
REFERENCES public.m (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkjpyqqd0vys4jayau98eij2xv3 FOREIGN KEY (c_id)
REFERENCES public.c (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fknjjuiq1kn44mu5299dn67t3np FOREIGN KEY (p_id)
REFERENCES public.p (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table C(大小 45k)
CREATE TABLE public.c
(
id bigint NOT NULL DEFAULT nextval('c_id_seq'::regclass),
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
m_id bigint,
CONSTRAINT c_pkey PRIMARY KEY (id),
CONSTRAINT fkbv33l9w17owvi5kgctqvaepn0 FOREIGN KEY (m_id)
REFERENCES public.m (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table M(尺码 200)
CREATE TABLE public.m
(
id bigint NOT NULL DEFAULT nextval('m_id_seq'::regclass),
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
a_id bigint,
CONSTRAINT m_pkey PRIMARY KEY (id),
CONSTRAINT fkikqmae593j3mruwqy84pc56is FOREIGN KEY (a_id)
REFERENCES public.a (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Table P(尺寸 5k)
CREATE TABLE public.p
(
id bigint NOT NULL,
created_by character varying(255),
created_date timestamp without time zone NOT NULL,
e character varying(255) NOT NULL,
a_id bigint,
CONSTRAINT p_pkey PRIMARY KEY (id),
CONSTRAINT fkehggtafv310ewdtcq772pwl01 FOREIGN KEY (a_id)
REFERENCES public.a (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
This is a special case of the common "top N per category" problem,在SQL中有一些解决方案。为什么不直接使用原生 SQL?您的查询可以转换为:
SELECT (t).*
FROM (
SELECT t, ROW_NUMBER() OVER (PARTITION BY t.m_id, t.p_id ORDER BY t.id DESC) rn
FROM t
JOIN c ON t.c_id = c.id
WHERE t.m_id = :m
AND c.created_date < :date
) t
WHERE t.rn = 1
主要区别在于您可以避免一次访问 T
table。
当然,我假设您拥有所有适当的索引,包括所有外键和 C.CREATED_DATE
列的索引。结果仍然可以映射到实体。
请注意,I'm using PostgreSQL specific syntax to nest records,这样再次从投影中删除 RN
列就容易多了。