简体   繁体   中英

SQL Server subquery alias can not be used and current code is slow

I have following data and code. What I am after is to get previous balance for a user based upon a date in summary table and then get the top 1 record from activity table where date is less than summary date. The code I have seems to give me the results needed, however, there is a big issue. My piece of code is extremely slow as there are 100s of thousands of rows in database and there could be any number of records which are less than a summary date. I tried to use joins and sub query but was getting errors that I could not use alias in subquery. Hope my question makes sense and some one can guide. Thanks Here is sample data and my query

if object_id('tempdb..#tsummary') is not null drop table #tsummary;
if object_id('tempdb..#tactivity') is not null drop table #tactivity;

--Temp tables to hold test data
CREATE TABLE #tsummary(
    [accountname] [char](8) NULL,
    [lastdepodate] [datetime] NULL,
    [depositAmount] [int] NULL,
    [currentBal] [int] NULL,
    [prevBal] [int] NOT NULL
) 

CREATE TABLE #tactivity(
    [accountName] [char](8) NULL,
    [activitydate] [datetime] NULL,
    [debitAmount] [money] NULL,
    [balance] [int] NULL
) 

--Insert test data for table 1
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User1   ', CAST(N'2018-01-25 16:09:05.000' AS DateTime), 20, 20, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User1   ', CAST(N'2018-01-06 17:43:58.000' AS DateTime), 20, 20, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User1   ', CAST(N'2018-01-05 22:39:46.000' AS DateTime), 3160, 3160, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User1   ', CAST(N'2018-01-04 16:45:30.000' AS DateTime), 3000, 3000, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User2   ', CAST(N'2017-10-06 11:33:54.000' AS DateTime), 6000, 6000, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User2   ', CAST(N'2017-10-06 11:23:20.000' AS DateTime), 4000, 20, 0);
INSERT #tsummary ([accountname], [lastdepodate], [depositAmount], [currentBal], [prevBal]) VALUES (N'User2   ', CAST(N'2017-09-24 10:48:49.000' AS DateTime), 500, 500, 0);

--Insert test data for table 1
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 17:43:58.000' AS DateTime), 0.0000, 20);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:41:26.000' AS DateTime), 120.0000, 1720);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:41:26.000' AS DateTime), 60.0000, 1660);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:28:32.000' AS DateTime), 60.0000, 1780);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:27:07.000' AS DateTime), 60.0000, 1840);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:25:54.000' AS DateTime), 60.0000, 1900);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:10:31.000' AS DateTime), 60.0000, 1920);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User1   ', CAST(N'2018-01-06 03:10:31.000' AS DateTime), 20.0000, 1900);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-06 11:23:20.000' AS DateTime), 0.0000, 20);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-05 15:41:46.000' AS DateTime), 400.0000, 420);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-04 17:03:16.000' AS DateTime), 100.0000, 520);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-04 13:25:06.000' AS DateTime), 400.0000, 920);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-04 13:20:45.000' AS DateTime), 120.0000, 1040);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-03 16:58:57.000' AS DateTime), 20.0000, 1060);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-03 11:26:24.000' AS DateTime), 200.0000, 1260);
INSERT #tactivity ([accountName], [activitydate], [debitAmount], [balance]) VALUES (N'User2   ', CAST(N'2017-10-01 13:11:51.000' AS DateTime), 100.0000, 1360);

--Following is my query which gives me prev balance
select mqry.accountName,mqry.lastDepoDate,mqry.depositAmount,mqry.currentBal,ISNULL(
    (select top 1 subq.balance from #tactivity subq where subq.accountName=mqry.accountName and subq.activityDate <mqry.lastDepoDate order by subq.activityDate desc)       
 ,0) prevBal
from #tsummary mqry;

The above query will give me following output

accountName lastDepoDate              depositAmount currentBal  prevBal
User1       2018-01-25 16:09:05.000   20             20         20
User1       2018-01-06 17:43:58.000   20             20         1660
User1       2018-01-05 22:39:46.000   3160           3160       0
User1       2018-01-04 16:45:30.000   3000           3000       0
User2       2017-10-06 11:33:54.000   6000           6000       20
User2       2017-10-06 11:23:20.000   4000           20         420
User2       2017-09-24 10:48:49.000   500            500        0

I have played around with joins and using subquery. But always get error as I must compare subquery with accountname and date from main query. Thanks for your suggestions in advance. Please pardon for lots of sample code, it is so that I can mimic my issue 100%

You can use outer apply :

select mqry.accountName, mqry.lastDepoDate, mqry.depositAmount, mqry.currentBal, 
       coalesce(subq.balance, 0) as prevBal
from #tsummary mqry outer apply
     (select top 1 subq.balance
      from #tactivity subq
      where subq.accountName = mqry.accountName and subq.activityDate < mqry.lastDepoDate
      order by subq.activityDate desc
     )  subq;

Then, for this query, you want an index on #tactivity :

create index idx_tactivity_accountName_activityDate_balance on #tactivity(accountName, activityDate, balance);

Actually, the index will help the subquery alone. But, it is fun to learn about apply (technically a lateral join ), because this is a very powerful operator.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM