First, I apologize if this has been answered elsewhere, but I was unable to find anything today. If it has been answered, the lack is me, not the search system.
I am having an issue where in a stored procedure works fairly quickly until I get to a specific point.
I have a database of POS sales data for a restaurant chain.
Among the various tables we have, the ones I need for this query are:
Item (the definitions of the various items; each row includes the SALES category the item belongs to; see caveats below)
Category (the definitions of the various categories items can be in)
CategoryItem (the mapping between the above)
HstItem (the historical sales of the items)
Caveats: There are 2 types of categories each item can be in:
sales categories: each item can be in one sales category at a time.
reporting categories: each item can be in a arbitrary number of these categories simultaneously.
I am needing to get sales totals for 6 date ranges for a specific REPORTING category (week to date, wtd ly, period to date, ptd ly, year to date, ytd ly).
All of that said, for all of my code the query / procedure runs in a decent amount of time, until to the section for the reporting category.
My select statement currently includes the following WHERE clause:
where hstitem.itemid in (select fkitemid from categoryitem where categoryitemid = ##)
I am looking for a more efficient / faster way to execute this.
Any assistance is greatly appreciated. Thanks in advance.
EDIT:
The full original query is as follows:
insert into #GnG (GStoreID, CurWkGNG, CurWkGNGLY,CurYTDGNG,CurYTDGNGLY,CurrPTDGNG,CurrPTDGNGLY)
select
hgi.FKStoreId,
CurWkGnG = sum(case when hgi.DateOfBusiness between #SDate and #EDate then hgi.price else 0 end),
CurWkGnGLY = sum(case when hgi.DateOfBusiness between #SDateLY and #EDateLY then hgi.price else 0 end),
CurYTDGnG =
case
when convert(varchar(10),opendate,126) between convert(varchar(10),#LYTDStart,126) and convert(varchar(10),#LYTDEnd,126) then sum(case when hgi.DateOfBusiness between DATEADD(day, (DATEPART(week, opendate) * 7 + DATEPART(weekday, opendate)) - (DATEPART(week, DATEADD(year, 1, opendate)) * 7 + DATEPART(weekday, DATEADD(year, 1, opendate))), DATEADD(year, 1, opendate)) and #CYTDEnd then hgi.price else 0 end)
else sum(case when hgi.DateOfBusiness between #CYTDStart and #CYTDEnd then hgi.price else 0 end)
end,
CurYTDGnGLY = sum(case when hgi.DateOfBusiness between #LYTDStart and #LYTDEnd then hgi.price else 0 end),
CurrPTDGnG = sum(case when hgi.DateOfBusiness between #CurrPtDStart and #CurrPtDEnd then hgi.price else 0 end),
CurrPTDGnGLY = sum(case when hgi.DateOfBusiness between #CurrPtDLYStart and #CurrPtDlyEnd then hgi.price else 0 end)
from hstGndItem hgi
join #StoresIncluded si
on hgi.FKStoreID = si.StoreID
where hgi.fkitemid in
(select fkitemid from categoryitem where categoryitemid = 25)
group by hgi.fkstoreid, opendate, comping
order by hgi.fkstoreid
Try converting the "IN" to a inner join like so :
FROM hstitem h inner join categoryitem c on c.fkitemid = h.itemid
where c.categoryitemid = ##
You can use WHERE EXISTS instead of IN to check if the ID exists in the table:
WHERE EXISTS
(
SELECT ci.fkitemid
FROM categoryitem ci
WHERE ci.categoryitemid = ## AND ci.fkitemid = hstitem.itemid
)
The difference between the IN clause and using EXISTS is that the sub-query inside the WHERE EXISTS will exit prematurely after a match have been found while the IN clause would wait until the sub-query is finished.
I apologize for the lag in my response here.
I found AN answer. Dont know if it is the right one or not, but it works for us.
I removed the code to use a sub select from the where, and am now generating a new table to hold the values that should pull. The new code to populate the table runs each morning around 0600. I then am having the main code simply join to that table to pull the single answer, rather than perform math based on the sub-query.
Thank you all for the suggestions.
Related
I have a complex SQL query and I want to manipulate resultant data based on certain conditions.
Here is a look at my data structure. It's a combination of 3 tables which allows storing of different activities and its registration period.
Here is a basic query (part of the more complex query):
select
act.ID, act.Name, arp.RegistrationPeriodId,
rp.StartDateTime, rp.EndDateTime
from
Activity act
join
ActivityRegistrationPeriod arp on arp.ActivityId = act.ID
join
RegistrationPeriod rp on rp.Id = arp.RegistrationPeriodId
What I want to achieve
I have to order this data in below conditions, priority vise.
Show programs that are now registering first, (so I guess it would be today is in between StartDateTime and EndDateTime)
Closing soon, (end date is nearest to today)
Opening soon (start data is nearest to today)
What I tried so far
I tried creating a temp table and storing data for each of these conditions (although that's not working properly) but I'm thinking of ordering it if possible and that's why I bring this issue here so a experienced SO can guide me.
Any help would be really appreciated. Thanks!
Updated query:
select
act.ID, act.Name, arp.RegistrationPeriodId,
rp.StartDateTime, rp.EndDateTime
from
Activity act
join
ActivityRegistrationPeriod arp on arp.ActivityId = act.ID
join
RegistrationPeriod rp on rp.Id = arp.RegistrationPeriodId
--where
-- act.AccountId = 3106
order by
(case when GETDATE() between StartDateTime and endDateTime then 1 else 2 end),
(case when rp.EndDateTime is null then 2 else 1 end),
rp.EndDateTime asc,
(case when rp.StartDateTime is null then 2 else 1 end),
rp.StartDateTime desc
You can use a case expression:
order by (case when current_date between StartDateTime and endDateTime then 1 else 2 end),
endDateTime asc,
StartDateTime desc
Right now I have a query that lets me know which users didn't make a purchase 12 months prior to becoming members. These users have MEM_PRE_12=0 and I want to filter off those users more natively using SQL partitions rather than always putting rudimentary WHERE criteria.
Here is the SQL I use to find the users I want/don't want.
SELECT SUM(CASE WHEN DATE <= DATEADD(month, -12, U.INSERTED_AT) THEN 1 ELSE 0 END) AS MEM_PRE_12, I.CLIENTID, I.INSTALLATIONID
FROM <<<My_Joined_Tables>>>
GROUP BY I.CLIENTID, I.INSTALLATIONID
HAVING MEM_PRE_12 != 0
ORDER BY MEM_PRE_12
After this I'm going to have to go back and say where I.CLIENTID in the above nested query and select the actual information I want from users who made purchases greater than their insertion date.
How can I do this without so much nesting of all these joined tables?
If you want the detailed rows for customers who made a purchase in the last 12 months, you can use window functions:
with q as (
<whatever your query logic is>
)
select q.*
from (select q.*,
SUM(CASE WHEN DATE <= DATEADD(month, -12, U.INSERTED_AT) THEN 1 ELSE 0 END) over (partition by CLIENTID, INSTALLATIONID) as AS MEM_PRE_12
from q
) q
where mem_pre_12 > 0;
I am creating a query that contains multiple sub-queries that show number of incidents in different status/category/etc. A date filter will need to be applied to all sub-queries, in order to count number of incidents created within the date range.
Because the report will be moved to Business Objects, I cannot specify the dates multiple times in the sub-queries. Hence I joined the incident table (inc) in the sub-queries with another incident table (inc_filter) in the outer query, and hoping to apply one date filter to all sub queries.
But the result returned was incorrect, I got multiple rows that have the value either 0 or 1.
Could anyone please point me to the right direction?
SELECT
(SELECT COUNT(*)
FROM Incident inc
WHERE inc.id = inc_filter.id
AND inc.status = 'Open')
"Total # of Open Inc",
(SELECT COUNT(*)
FROM Incident inc
WHERE inc.id = inc_filter.id
AND inc.status = 'Closed')
"Total # of Closed Inc"
--more sub-queries here...
FROM Incident inc_filter
AND inc_filter.CREATED > '10-Apr-2017'
AND inc_filter.CREATED < '13-Apr-2017'
You are probably simply looking for conditional aggregation:
SELECT
COUNT(CASE WHEN status = 'Open' THEN 1 END) AS "Total # of Open Inc",
COUNT(CASE WHEN status = 'Closed' THEN 1 END) AS "Total # of Closed Inc"
-- more counts here...
FROM Incident
WHERE created >= DATE '2017-04-10' AND created < DATE '2017-04-13';
First you should use case and you dont need use many subquery,
second if i understood your question you should use sum() like this
SELECT sum(case when inc_filter.status = 'Open' then 1 else 0 end) as open,
sum(case when inc_filter.status = 'Closed' then 1 else 0 end) as closed
FROM Incident inc_filter
AND inc_filter.CREATED > '10-Apr-2017'
AND inc_filter.CREATED < '13-Apr-2017'
Dear you need one more select above this with using sum of your count. actually you are using group function but that is apply on row level. you need you sum all your subquery columns to see the required result for example your query will be
select sum("Total # of Open Inc") ,sum("Total # of Closed Inc")
from(
SELECT
(SELECT COUNT(*)
FROM Incident inc
WHERE inc.id = inc_filter.id
AND inc.status = 'Open')
"Total # of Open Inc",
(SELECT COUNT(*)
FROM Incident inc
WHERE inc.id = inc_filter.id
AND inc.status = 'Closed')
"Total # of Closed Inc"
--more sub-queries here...
FROM Incident inc_filter
AND inc_filter.CREATED > '10-Apr-2017'
AND inc_filter.CREATED < '13-Apr-2017');
But it is better for you to use joins
i have a select statement that contains hundred thousands if data, however the execution time is very slow which take longer than 15 minutes. Is the any way that i can improve the execution time for this select statement.
select a.levelP,
a.code,
a.descP,
(select nvl(SUM(amount),0) from ca_glopen where code = a.code and acc_mth = '2016' ) ocf,
(select nvl(SUM(amount),0) from ca_glmaintrx where code = a.code and to_char(doc_date,'yyyy') = '2016' and to_char(doc_date,'yyyymm') < '201601') bcf,
(select nvl(SUM(amount),0) from ca_glmaintrx where jum_amaun > 0 and code = a.code and to_char(doc_date,'yyyymm') = '201601' ) debit,
(select nvl(SUM(amount),0) from ca_glmaintrx where jum_amaun < 0 and code = a.code and to_char(doc_date,'yyyymm') = '201601' ) credit
from ca_chartAcc a
where a.code is not null
order by to_number(a.code), to_number(levelP)
please help me for the way to up speed my query and result.TQ
Your primary problem is that most of your subqueries use functions on your search criteria, including some awkward ones on your dates. It's much better to flip that around and explicitly qualify the expected range, by supplying actual dates (a one month range is usually a small percentage of total rows, so this is very likely to hit an index).
SELECT Chart.levelP, Chart.code, Chart.descP,
COALESCE(GL_SUM.ocf, 0),
COALESCE(Transactions.bcf, 0),
COALESCE(Transactions.debit, 0),
COALESCE(Transactions.credit, 0),
FROM ca_ChartAcc Chart
LEFT JOIN (SELECT code, SUM(amount) AS ocf
FROM ca_GLOpen
WHERE acc_mth = '2016') GL_Sum
ON GL_Sum.code = Chart.code
LEFT JOIN (SELECT code,
SUM(amount) AS bcf,
SUM(CASE WHEN amount > 0 THEN amount) AS debit,
SUM(CASE WHEN amount < 0 THEN amount) AS credit,
FROM ca_GLMainTrx
WHERE doc_date >= TO_DATE('2016-01-01')
AND doc_date < TO_DATE('2016-02-01')) Transactions
ON Transactions.code = Chart.code
WHERE Chart.code IS NOT NULL
ORDER BY TO_NUMBER(Chart.code), TO_NUMBER(Chart.levelP)
If you only need a few codes, it may yield better results to push those values into the subqueries as well (although note that the optimizer is likely to perform this for you).
It may be possible to remove the calls to TO_NUMBER(...) from the ORDER BY clause; however, this depends on the format of the values, since how they were encoded may change the ordering of results.
Hello guys I am sorry but I didn’t know what I should call this question.
I have a table that contains information one these are how long it took from when the row was created and until it was last updated these are shown within the following columns:
CREATED
LAST_UPD
The time difference between these are shown in a separate column called:
SOLVED_SEC
(The time is shown in seconds)
Now I want to collect some of the data from this table but should the CREATED (which is a date) be outside of our company’s opening hours, the SOLVED_SEC should recalculated in my
Our opening hours exists in a table called KS_DRIFT.SYS_DATE_KS.
This table has a column named: THIS_DATE_OPENING.
I was thinking that I could calculate the new solved time as such:
THIS_DATE_OPENING-LAST_UPD
However I’m not quite sure how to do this
The following is the SQL that i have right now:
SELECT
TIDSPUNKT, LAST_UPD, AA.CREATED,
TRUNC(AA.SOLVED_SEC/60/60,2) as LØST_TIME,
//this is my attempt
CASE
WHEN AA.CREATED >= CC.THIS_DATE_CLOSING
THEN LØST_TIME = (LAST_UPD-CC.THIS_DATE_OPENING) AS LØST_TIME
END,
COUNT(CASE WHEN AA.LAST_UPD >= CC.THIS_DATE_CLOSING THEN 1 END) as AFTER_CLOSING,
COUNT(CASE WHEN STATUS ='Færdig' THEN 1 END)as Completed_Callbacks
FROM
KS_DRIFT.NYK_SIEBEL_CALLBACK_AGENT_H_V AA
INNER JOIN
KS_DRIFT.V_TEAM_DATO BB ON AA.TIDSPUNKT = BB.DATO
RIGHT JOIN
KS_DRIFT.SYS_DATE_KS CC ON AA.TIDSPUNKT = CC.THIS_DATE
WHERE
AA.TIDSPUNKT BETWEEN '2012-04-01' AND '2013-04-04'
AND AA.AFSLUTTET_AF = BB.INITIALER
GROUP BY
AA.TIDSPUNKT, LØST_SEKUNDER, LAST_UPD, AA.CREATED
Sadly this doesn’t work.
My question is how can I change the value of SOLVED_SEC if the CREATED > THIS_DATE_CLOSED ?
Should you require additional information please do not hesitate to comment.
UPDATE
I have tried the following:
CASE WHEN (AA.CREATED >= CC.THIS_DATE_CLOSING) THEN (AA.LAST_UPD-CC.THIS_DATE_OPENING) END AS SOVLED_AFTER_OPENING
However i get
"not a GROUP BY expression"
UPDATE 2
My SQL statement now looks like this:
SELECT TIDSPUNKT,
AA.CREATED,
LAST_UPD,
AA.AGENTGRUPPE,
TRUNC(AA.LØST_SEKUNDER/60/60,2) as LØST_TIME,
'LØST_TIME' = CASE WHEN AA.CREATED >= CC.THIS_DATE_CLOSING THEN DATEDIFF(ss, CC.THIS_DATE_OPENING, LAST_UPD) END,
COUNT(CASE WHEN AA.AGENTGRUPPE not in('Hovednumre','Privatcentre','Forsikring','Hotline','Stabe','Kunder','Erhverv','NykreditKunder','Servicecentret') THEN 1 END) as CALLBACKS_OUTSIDE_OF_KS,
COUNT(CASE WHEN AA.CREATED >= CC.THIS_DATE_CLOSING THEN 1 END) as AFTER_CLOSING,
COUNT(CASE WHEN STATUS ='Færdig' THEN 1 END)as Completed_Callbacks
FROM KS_DRIFT.NYK_SIEBEL_CALLBACK_AGENT_H_V AA
INNER JOIN KS_DRIFT.V_TEAM_DATO BB ON AA.TIDSPUNKT = BB.DATO
RIGHT JOIN KS_DRIFT.SYS_DATE_KS CC ON AA.TIDSPUNKT = CC.THIS_DATE
WHERE AA.TIDSPUNKT BETWEEN '2013-04-01' AND '2013-04-04'
AND AA.AFSLUTTET_AF = BB.INITIALER
GROUP BY AA.TIDSPUNKT, LAST_UPD, AA.CREATED, AA.LØST_SEKUNDER,
AA.AFSLUTTET_AF, AA.AGENTGRUPPE
However i get From keyword not found where expected
You need to use the DATEDIFF function when trying to calculate differences in time.
For example:
SELECT DATEDIFF(ss, THIS_DATE_OPENING, LAST_UPD);
Where "ss" denotes seconds. This first parameter is the datepart that you want to calculate. Like seconds or minutes or days or whatever.
You can find the documentation here http://msdn.microsoft.com/en-us/library/aa258269(v=sql.80).aspx
Let me know if I didn't understand your question correctly.
I also just noticed that you're trying to do this:
CASE WHEN AA.CREATED >= CC.THIS_DATE_CLOSING THEN LØST_TIME =(LAST_UPD-CC.THIS_DATE_OPENING) AS LØST_TIME END
Try this instead:
'L0ST_TIME' = CASE WHEN AA.CREATED >= CC.THIS_DATE_CLOSING THEN DATEDIFF(ss, CC.THIS_DATE_OPENING, LAST_UPD) END
HTH