SQL Server Optimize sub-queries in select statement columns

beq87vna  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(133)

Below query has two sub-queries in select statement and those are getting executed for each row. This is very inefficient and is there any way to optimize this in SQL Server?

SELECT 
  *
FROM
(
SELECT
  t.id,
  t.status,
  CASE
    WHEN (SELECT TOP 1 tt.code from text_view tt  where tt.act_id = t.id   ORDER BY tt.created_dt DESC) IN ('A', 'B', 'C','D','E') THEN
      (SELECT TOP 1 created_dt from text_view tt where tt.act_id = t.id  ORDER BY tt.created_dt DESC)
    ELSE
      NULL
  END as notedate,
  (select top 1 created_dt from act_mail tm where tm.act_id = t.id order by created_dt ASC ) AS [mail_dt], 
FROM activity t
WHERE  t.status not in ('CLOSED', 'ACT', 'CANCELED')
) q
WHERE q.notedate is not null
voj3qocg

voj3qocg1#

We can try using a join approach here:

WITH cte1 AS (
    SELECT code, created_dt, act_id,
           ROW_NUMBER() OVER (PARTITION BY act_id ORDER BY created_dt DESC) rn
    FROM text_view
),
cte2 AS (
    SELECT created_dt, act_id,
           ROW_NUMBER() OVER (PARTITION BY act_id ORDER BY created_dt) rn
    FROM act_mail
)

SELECT DISTINCT
    a.id,
    a.status,
    CASE WHEN t1.code IN ('A', 'B', 'C', 'D', 'E') THEN t1.created_dt END AS notedate,
    t2.created_dt AS mail_dt
FROM activity a
LEFT JOIN cte1 t1
    ON t1.act_id = a.id AND t1.rn = 1
LEFT JOIN cte2 t2
    ON t2.act_id = a.id and t2.rn = 1
WHERE a.status NOT IN ('CLOSED', 'ACT', 'CANCELED') AND
      t1.code IN ('A', 'B', 'C', 'D', 'E') AND
      t1.created_dt IS NOT NULL;
yjghlzjz

yjghlzjz2#

You can take advantage of NOT EXISTS to find the latest record from the TEXT_VIEW table and then use LEFT JOIN and GROUP BY to get the minimum date from ACT_MAIL table as follows:

SELECT
    T.ID,
    T.STATUS,
    TT.CREATED_DT AS NOTEDATE,
    MIN(TM.CREATED_DT) AS MAIL_DT
  FROM
         ACTIVITY T
      JOIN TEXT_VIEW TT ON TT.ACT_ID = T.ID
      LEFT JOIN ACT_MAIL  TM ON TM.ACT_ID = T.ID
 WHERE
    T.STATUS NOT IN ( 'CLOSED', 'ACT', 'CANCELED' )
       AND TT.CODE IN ( 'A', 'B', 'C', 'D', 'E' )
       AND NOT EXISTS (
        SELECT
            1
          FROM
            TEXT_VIEW TTIN
         WHERE
                TTIN.ACT_ID = TT.ACT_ID
               AND TTIN.CREATED_DT > TT.CREATED_DT
    )
 GROUP BY
    T.ID,
    T.STATUS,
    TT.CREATED_DT
vpfxa7rd

vpfxa7rd3#

You want to select activities the latest text view of which has a code A-E. Probably the simplest method to get the latest text view is a CROSS APPLY (aka lateral inner join).

You also want to know the oldest mail date. This is just MIN(created_dt) . You could get this with an OUTER APPLY (aka lateral left outer join), but as this is about a single column, you can just as well keep this in your SELECT clause.

SELECT
  a.id,
  a.status,
  ltv.created_dt,
  (SELECT MIN(created_dt) FROM act_mail am WHERE am.act_id = a.id) AS mail_dt
FROM activity a
CROSS APPLY
(
  SELECT TOP(1) *
  FROM text_view tv
  WHERE tv.act_id = a.id
  ORDER BY tv.created_dt DESC
) ltv
WHERE a.status not in ('CLOSED', 'ACT', 'CANCELED')
AND ltv.code IN ('A', 'B', 'C', 'D', 'E');

As for a well performing query, you may want these indexex:

CREATE INDEX idx1 ON activity (status, id);
CREATE INDEX idx2 ON act_mail (act_id, created_dt);
CREATE INDEX idx3 ON text_view (act_id, created_dt, code);

相关问题