JPQL с предложением Group by в базе данных SQL Server не работает

#sql-server #spring-data-jpa #jpql #jpa-2.0

Вопрос:

Я новичок в базе данных SQL Server. Приведенный ниже запрос JPQL не выполняется и не выдает никаких ошибок. Просто мое приложение зависает при этом запросе. Этот код отлично работает с базой данных MySQL, но не с базой данных SQL Server (2019).

 @Query(value = "SELECT R.riskType, count(distinct V.bname) FROM RiskViolation V JOIN V.job J JOIN V.risk R WHERE J.id = ?1 GROUP BY R.riskType ")
public List<Object[]> sodUserByRiskType(Long jobId);
 

Но когда я запускаю приведенный ниже преобразованный sql-запрос непосредственно в базе данных SQL Server, он работает нормально.

 select count(distinct riskviolat0_.bname) as col_1_0_, risklog2_.risk_type as col_0_0_ from risk_violation riskviolat0_ inner join analysis_job analysisjo1_ on riskviolat0_.job_id=analysisjo1_.id inner join risk_log risklog2_ on riskviolat0_.risk_id=risklog2_.id where analysisjo1_.id=? group by risklog2_.risk_type;
 

Вот классы сущностей Java, которые используются в запросе JPQL:

RiskViolation.java

 @Entity
@Table(name = "risk_violation")
public class RiskViolation {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "job_id")
private AnalysisJob job;

private String bname;

private String riskName;

private String violations;

@Column(name = "violated", columnDefinition = "BIT", length = 1)
private boolean violated;

@Column(name = "simulation", columnDefinition = "BIT", length = 1)
private boolean simulation;

@Column(name = "mitigation_name")
private String mitigationName;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "risk_id")
private RiskLog risk;

@JsonIgnore
@OneToMany(mappedBy = "riskViolation", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<RuleViolation> ruleViolations;
}
 

AnalysisJob.java

 @Entity
@Table(name = "analysis_job")
public class AnalysisJob {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "profile_name")
private String profileName;

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "profile_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private AnalysisProfileLog profileLog;

@Column(name = "description")
private String description;

@Column(name = "status")
private String status;

@Column(name = "started_on")
private Date startedOn;

@Column(name = "completed_on")
private Date completedOn;

@Column(name = "completion_message")
private String completionMessage;

@Column(name = "percent_completed")
private float percentCompleted;

@Column(name = "run_by")
private String runBy;

@Column(name = "removed", columnDefinition = "BIT", length = 1)
private boolean removed;

@OneToMany(mappedBy = "job", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
private List<JobResultData> resultData;

@Column(name = "pos_analysis", columnDefinition = "BIT", length = 1)
private boolean posAnalysis;

@Column(name = "submitted", columnDefinition = "BIT", length = 1)
private boolean submitted;

@Column(name = "position_id")
private String positionId;
}
 

RiskLog.java

 @Entity
@Table(name = "risk_log")
public class RiskLog {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Long jobId;

private String name;

private String riskDescription;

private String riskCondition;

private String businessProcess;

@Column(name = "business_sub_process")
private String subProc;

private String riskType;

@JsonIgnore
@OneToMany(mappedBy = "riskLog", cascade = CascadeType.ALL)
protected List<RuleLog> rules;
}
 

Нужно ли мне вносить какие-либо изменения в классы запросов и сущностей, чтобы это работало?

Вот URL-адрес плана запроса

Предполагаемый план выполнения

Фактический план выполнения

План запроса с фактическими строками

Комментарии:

1. Пожалуйста, поделитесь планом запроса через brentozar.com/pastetheplan . Чтобы получить план выполнения запроса, запустите его в SSMS, пока Jpql выполняет запрос select qp.query_plan from sys.dm_exec_requests r cross apply sys.dm_exec_query_plan(r.plan_handle) qp where r.session_id <> @@SPID

2. Спасибо @Charlieface за ваш ответ. Я обновил свой вопрос, указав URL-адрес плана запроса внизу.

3. Попробуйте индекс risk_violation (job_id, bname) , вы больше ничего не можете сделать. можете ли вы загрузить «фактический» план выполнения из SSMS? Сколько строк составляет конечный результирующий набор, я не вижу из расчетного плана?

4. Добавлен индекс, как вы предложили, но все тот же. Я не уверен, что правильно выполняю план запроса. На этот раз я выбрал опции «Включить фактический план выполнения» и «Включить статистику запросов в реальном времени» на панели инструментов и выполнил. Пожалуйста, ознакомьтесь с обновленным вопросом с новыми планами запросов.

5. Теперь я должен знать, как получить фактический план запроса. Дайте мне несколько минут, я добавлю это.

Ответ №1:

Это сработало после исключения класса facade между классом Java (где находится моя бизнес-логика) и репозиторием JPA (используется для выполнения запросов JPQL). Прямой вызов метода репозитория JPA из класса Java. Не уверен, почему он не работал с классом facade с SQL Server, но то же самое работало с базой данных MySQL.