How to reduce the fetching time in view table? - sql

I have tried to fetch the data from view table .I have executed select query in phpmyadmin .It takes 5.2875 seconds. But normal table takes only 0.0300 seconds.
Query :
SELECT line FROM `summary_view` WHERE date='2022-02-25'
CREATE VIEW summary_view AS
select
adh.id AS id,
adh.line AS line,
fam.area AS area,
fam.sub_area AS sub_area,
fam.family AS family,
pro.produced AS produced,
pro.service AS service,
round(((testdb.down * 60) / adh.takt_time),0) AS units_lost,
round(((((adh.worked_time * 60) - adh.break_time) * 60) / adh.takt_time),0) AS oa_capacity,
round(((((adh.plan_time * 60) - adh.break_time) * 60) / adh.takt_time),0) AS ay_capacity,
testdb.machines AS machines,
testdb.manpower AS manpower,
testdb.material AS material,
testdb.methods AS methods,
testdb.misc AS misc,
testdb.down AS down,
round((((pro.produced + pro.service) * adh.takt_time) / 60),0) AS uptime,
adh.break_time AS break_time,
round(((adh.worked_time * 60) - (((((pro.produced + pro.service) * adh.takt_time) / 60) + testdb.down) + adh.break_time)),0) AS minute_error,
round((adh.worked_time * 60),0) AS working_time,
round(((adh.worked_time * 60) - adh.break_time),0) AS worked_time,
round(((adh.plan_time * 60) - adh.break_time),0) AS plan_time,
round(adh.takt_time,0) AS takt_time,
NULL AS headcount,
rep.nff AS nff,
rep.dpu AS dpu,
round(((((pro.produced + pro.service) * adh.takt_time) / 60) / ((adh.worked_time * 60) - adh.break_time)),4) AS oa,
round(((((pro.produced + pro.service) * adh.takt_time) / 60) / ((adh.plan_time * 60) - adh.break_time)),4) AS ay,
adh.shift AS shift,
GET_MONTH(adh.date) AS month,
GET_WEEK(adh.date) AS week,
adh.date AS date,
adh.skey AS skey
from
(
(
(
(
adhoc adh
left join
(
select
sum(if((down.category = 'Machines'),down.duration,NULL)) AS machines,
sum(if((down.category = 'Manpower'),down.duration,NULL)) AS manpower,
sum(if((down.category = 'Material'),down.duration,NULL)) AS material,
sum(if((down.category = 'Methods'),down.duration,NULL)) AS methods,
sum(if((down.category = 'Misc'),down.duration,NULL)) AS misc,
sum(down.duration) AS down,
down.skey AS skey
from (down join family on((down.line = family.line)))
where ((down.reason <> 'Scheduled Shutdown') and (family.area in ('Indoor','Outdoor','Specialty')))
group by down.skey
)
testdb on((adh.skey = testdb.skey))
)
left join
(
select
count(if((prod.repair_flag <> 'S'),1,NULL)) AS produced,
count(if((prod.repair_flag = 'S'),1,NULL)) AS service,
prod.skey AS skey
from (prod join family on((prod.line = family.line)))
group by prod.skey
)
pro on((adh.skey = pro.skey))
)
left join
(
select
count(if((repair.level_one = 'No Fault Found'),1,NULL)) AS nff,
count(if(((repair.level_one <> 'No Fault Found') and (repair.level_two <> 'Reclaim Refrigerant')),1,NULL)) AS dpu,
repair.skey AS skey
from repair
group by repair.skey
)
rep on((adh.skey = rep.skey))
)
join family fam on((adh.line = fam.line))
)
where (adh.area = 'Assembly')
;
How to reduce the query execution time in view table ?
Is there any way to reduce the execution time without adding index ?

Related

How to avoid function repetition in SELECT, GROUP BY, and ORDER BY in SQL

I am writing a statistical query where the value is duplicated in SELECT, GROUP BY, and ORDER BY. Having to repeat the same value makes it hard to read the query and modify it.
How can I avoid repeating FLOOR(COALESCE(LEN(Body), 0) / 100) 3-4 times in the query below.
SELECT FLOOR(COALESCE(LEN(Body), 0) / 100) * 100 as BodyLengthStart,
(FLOOR(COALESCE(LEN(Body), 0) / 100) + 1) * 100 - 1 as BodyLengthEnd,
COUNT(*) as MessageCount
FROM [Message]
GROUP BY FLOOR(COALESCE(LEN(Body), 0) / 100)
ORDER BY FLOOR(COALESCE(LEN(Body), 0) / 100)
The output of the query is the number of messages bucketed by how many hundreds of characters they have.
BodyLengthStart
BodyLengthEnd
MessageCount
0
99
130
100
199
76
200
299
36
Using CROSS APPLYs
SELECT BodyLengthStart,
BodyLengthEnd,
COUNT(*)
FROM [Message]
CROSS APPLY (
VALUES
(FLOOR(COALESCE(LEN(Body), 0) / 100))
) a1(v)
CROSS APPLY (
VALUES
(v * 100, (v + 1) * 100 - 1)
) a2(BodyLengthStart, BodyLengthEnd)
GROUP BY BodyLengthStart,
BodyLengthEnd
One option may be a CTE (Common Table Expression), something along these lines:
WITH x AS
(
SELECT FLOOR(COALESCE(LEN(Body), 0) / 100) AS BodyLength
FROM [Message]
)
SELECT BodyLength * 100 AS BodyLengthStart,
(BodyLength + 1) * 100 - 1 AS BodyLengthEnd,
COUNT(*) as MessageCount
FROM x
GROUP BY BodyLength
ORDER BY BodyLength
As a side note - if the statement prior to this doesn't end with a semi-colon (;), this will not work as expected.
Use a sub-select:
SELECT BodyLengthStart,
BodyLengthEnd,
COUNT(*)
FROM (SELECT FLOOR(COALESCE(LEN(Body), 0) / 100) * 100 as BodyLengthStart,
(FLOOR(COALESCE(LEN(Body), 0) / 100) + 1) * 100 - 1 as BodyLengthEnd
FROM [Message]) as a
GROUP BY BodyLengthStart,
BodyLengthEnd
You can define a SELECT after the FROM; in this way, you can elaborate previously your data.
You can use a common table expression:
WITH cte AS
(
SELECT FLOOR(COALESCE(LEN(Body), 0) / 100) * 100 as BodyLengthStart,
(FLOOR(COALESCE(LEN(Body), 0) / 100) + 1) * 100 - 1 as BodyLengthEnd
FROM [Message]
)
SELECT BodyLengthStart,BodyLengthEnd,COUNT(*)
FROM cte
GROUP BY BodyLengthStart,BodyLengthEnd

I want to select the Last 10 Average Weights

Below is my query but when I include my CapturedDateTime and IsOutOfSpec columns then it returns multiple rows but I only want one record to be returned that displays the averages of the last 10 records. So it must return the average PartWeight, average SprueWeight and average Bom Weight for a specified stock code.
--Last 10 Average Report
SELECT Top 10 c.StockCode,i.LongDesc,
AVG(c.PartWeightGram) AS 'Part Weight Average',
AVG(c.SprueWeightGram) AS 'Sprue Weight Average' ,
AVG(c.TolerancePercentage) AS 'Tolerance Average' ,
AVG(c.BomWeightKG * 1000) AS 'Bom Weight Average' ,
AVG(c.PartWeightGram + (c.SprueWeightGram / 2)) - (c.BomWeightKG) AS 'Variance To Syspro Average',
AVG((((((c.PartWeightGram + c.SprueWeightGram / 2))) - (c.BomWeightKG * 1000)) / (c.BomWeightKG * 1000)) * 100) AS 'VarianceToSysproPct'
FROM tblComponentWeightCheck c
LEFT JOIN [Mercury].[EncoreCompanyA].dbo.InvMaster i ON c.StockCode = i.StockCode
WHERE c.StockCode='000-256966-020' And Deleted = 'False'
GROUP BY c.StockCode,i.LongDesc,c.BomWeightKG
ORDER BY c.StockCode DESC
This is my Table:
SELECT [ComponentWeightCheckID]
,[EmpID]
,[StockCode]
,[Process]
,[PartWeightGram]
,[SprueWeightGram]
,[BomWeightKG]
,[TolerancePercentage]
,[AssetID]
,[CapturedDateTime]
,[Hostname]
,[Username]
,[IsOutOfSpec]
,[Tool]
,[Deleted]
FROM [dbo].[tblComponentWeightCheck]
Sample Data:
SELECT Top 10 c.StockCode,i.LongDesc AS 'Description',
(SELECT TOP 10 AVG(PartWeightGram) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Part Weight Average',
(SELECT TOP 10 AVG([SprueWeightGram]) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Sprue Weight Average',
(SELECT TOP 10 AVG(c.TolerancePercentage) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Tolerance Average',
(SELECT TOP 10 AVG([BomWeightKG]) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Bom Weight Average',
(SELECT TOP 10 AVG((c.PartWeightGram + (c.SprueWeightGram / 2)) - (c.BomWeightKG * 1000)) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Variance To Syspro Average',
(SELECT TOP 10 AVG((((((c.PartWeightGram + c.SprueWeightGram / 2))) - (c.BomWeightKG * 1000)) / (c.BomWeightKG * 1000)) * 100) FROM tblComponentWeightCheck c WHERE c.StockCode = '000-256966-020' GROUP BY c.StockCode ) AS 'Variance To Syspro & Average'
FROM tblComponentWeightCheck c
LEFT JOIN [Mercury].[EncoreCompanyA].dbo.InvMaster i ON c.StockCode = i.StockCode
WHERE c.StockCode = '000-256966-020'
AND CONVERT(Date, c.CapturedDateTime) Between '2021-04-01' AND '2021-05-24'
GROUP BY c.StockCode, i.LongDesc
This image shows the result of the above query.It only returns 1 row which it should but it is missing my CapturedDateTime AND IsOutOfSpec columns, as soon as i include those then it returns multiple records which i don't want
Presumably, the LEFT JOIN is not needed -- it would seem odd to have stock codes that are not in the inventory master.
You can do this pretty easily using APPLY:
SELECT Top 10 i.StockCode, i.LongDesc,
AVG(c.PartWeightGram) AS Part_Weight_Average,
AVG(c.SprueWeightGram) AS Sprue_Weight_Average,
AVG(c.TolerancePercentage) AS Tolerance_Average,
AVG(c.BomWeightKG * 1000) AS Bom_Weight_Average,
AVG(c.PartWeightGram + (c.SprueWeightGram / 2)) - (c.BomWeightKG) AS Variance_To_Syspro_Average,
AVG((((((c.PartWeightGram + c.SprueWeightGram / 2))) - (c.BomWeightKG * 1000)) / (c.BomWeightKG * 1000)) * 100) AS VarianceToSysproPct
FROM [Mercury].[EncoreCompanyA].dbo.InvMaster i CROSS APPLY
(SELECT TOP (10) c.*
FROM tblComponentWeightCheck c
WHERE c.StockCode = i.StockCode
ORDER BY c.CapturedDateTime DESC
) c
WHERE i.StockCode = '000-256966-020' AND Deleted = 'False'
GROUP BY i.StockCode, i.LongDesc, c.BomWeightKG
ORDER BY i.StockCode DESC;
For performance, I recommend indexes on tblComponentWeightCheck(StockCode, CapturedDateTime DESC) and InvMaster(StockCode).
Judging by your query, you should use a window function and a subquery instead of top 10. Following an example. However, you will have to provide the sorting criteria for the ROW_NUMBER() in order to define your "TOP 10"...
--Last 10 Average Report
SELECT c.StockCode,c.LongDesc,
AVG(c.PartWeightGram) AS 'Part Weight Average',
AVG(c.SprueWeightGram) AS 'Sprue Weight Average' ,
AVG(c.TolerancePercentage) AS 'Tolerance Average' ,
AVG(c.BomWeightKG * 1000) AS 'Bom Weight Average' ,
AVG(c.PartWeightGram + (c.SprueWeightGram / 2)) - (c.BomWeightKG) AS 'Variance To Syspro Average',
AVG((((((c.PartWeightGram + c.SprueWeightGram / 2))) - (c.BomWeightKG * 1000)) / (c.BomWeightKG * 1000)) * 100) AS 'VarianceToSysproPct'
FROM (
SELECT c.StockCode
,i.LongDesc
,c.PartWeightGram
,c.SprueWeightGram
,c.TolerancePercentage
,c.BomWeightKG
,c.PartWeightGram
,c.PartWeightGram
,ROW_NUMBER() OVER (PARTITION BY c.StockCode ORDER BY ...) AS rn
FROM tblComponentWeightCheck c
LEFT JOIN [Mercury].[EncoreCompanyA].dbo.InvMaster i ON c.StockCode = i.StockCode
WHERE Deleted = 'False'
) c
WHERE c.StockCode='000-256966-020'
AND rn <= 10
GROUP BY c.StockCode,c.LongDesc,c.BomWeightKG
ORDER BY c.StockCode DESC

How to do a left outer join on inequality?

I have the following SQL query on BigQuery. I am trying to join two different tables one of which is a much smaller table than the other. At first I used the regular join but this results in elimination of some of the data that I am working with.
with weekly_periods as(
select
ticket_id,
start_time_in_minutes_from_week,
raw_delta_in_minutes,
week_number,
greatest(0, start_time_in_minutes_from_week - week_number * (7 * 24 * 60)) as ticket_week_start_time,
least(start_time_in_minutes_from_week + raw_delta_in_minutes - week_number * (7 * 24 * 60),(7 * 24 * 60)) as ticket_week_end_time
from
ticket_solved_time,
unnest(generate_array(0, floor((start_time_in_minutes_from_week + raw_delta_in_minutes) / (7 * 24 * 60)), 1)) as week_number,
intercepted_periods as(
select
ticket_id,
week_number,
ticket_week_start_time,
ticket_week_end_time,
schedule.start_time as schedule_start_time,
schedule.end_time as schedule_end_time,
least(ticket_week_end_time, schedule.end_time) - greatest(ticket_week_start_time, schedule.start_time) as scheduled_minutes
from
weekly_periods
left join
schedule
on ticket_week_start_time <= schedule.end_time
and ticket_week_end_time >= schedule.start_time
But I am receiving an error of: -- LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join --
How would it be possible to do this join while preserving the data? If I just do JOIN the query doesn't not return the full result.
Thank you!
What you need to do is use a cross join and then add your join condition in where clause as shown below:
with weekly_periods as(
select
ticket_id,
start_time_in_minutes_from_week,
raw_delta_in_minutes,
week_number,
greatest(0, start_time_in_minutes_from_week - week_number * (7 * 24 * 60)) as ticket_week_start_time,
least(start_time_in_minutes_from_week + raw_delta_in_minutes - week_number * (7 * 24 * 60),(7 * 24 * 60)) as ticket_week_end_time
from
ticket_solved_time,
unnest(generate_array(0, floor((start_time_in_minutes_from_week + raw_delta_in_minutes) / (7 * 24 * 60)), 1)) as week_number,
intercepted_periods as(
select
ticket_id,
week_number,
ticket_week_start_time,
ticket_week_end_time,
schedule.start_time as schedule_start_time,
schedule.end_time as schedule_end_time,
least(ticket_week_end_time, schedule.end_time) - greatest(ticket_week_start_time, schedule.start_time) as scheduled_minutes
from
weekly_periods
Cross join
schedule
Where ticket_week_start_time <= schedule.end_time
and ticket_week_end_time >= schedule.start_time
You need to join the tables based on a relational key using an = operator (example below) then use where to implement your criteria... Since you didn't post your table structure, this is just an example of the correct way to join
left join
schedule
on schedule.id = weekly_period.ticketid and
weekly_period.ticketid = intercepted_period.ticketid
where
ticket_week_start_time <= schedule.end_time
and ticket_week_end_time >= schedule.start_time

ORA-00937 not a single-group group function converted from SQL Server

I'm getting an ORA-00937 error when running the following ORACLE statement, I'm very new to Oracle (T-SQL is more my bag). I've converted a select statement previously written in SQL I'm at a loss as to why it's expecting a group by ... as all columns are sum'd. This is the converted select statement (I'm currently running/ testing these using SQLPLUS) :
select
round(COALESCE(
(COALESCE((select SUM((F_BOOK_DATES.BKD_ADATE_ENDS - F_BOOK_DATES.BKD_ADATE_START) * (60 * 24)) FROM F_BOOK_DATES
INNER JOIN F_BOOK_HEADER ON F_BOOK_DATES.BKD_FKEY_BK_SEQ = F_BOOK_HEADER.BK_SEQ
and EXTRACT (YEAR from F_BOOK_DATES.BKD_DATE_START ) = EXTRACT (YEAR from SYSDATE )
and EXTRACT (MONTH from F_BOOK_DATES.BKD_DATE_START ) = EXTRACT (MONTH from SYSDATE )
and EXTRACT (DAY from F_BOOK_DATES.BKD_DATE_START ) = EXTRACT (DAY from SYSDATE )
AND F_BOOK_HEADER.BK_STATUS NOT IN ('CX','TP')
AND F_BOOK_HEADER.Deleted <> 1
AND F_BOOK_DATES.Deleted <> 1),9999999999.99,0)
/sum(case when FAREALO.AllDayBooking = 1 then 1400 else
((FAREALO.LO_TIME_END - FAREALO.LO_TIME_START) * (60 * 24)) end) * 100),0),2) as "PercentUtilised"
from FAREALO
inner join F_LO_TYPE on FAREALO.LO_FKEY_LOT_SEQ = F_LO_TYPE.LOT_SEQ
where FAREALO.LO_BK_LOCATION = 1
and LOT_CBS = 1;
This is the SQL statement (that works) that I tried to convert above:
select
round(isnull(
(isnull(convert (DECIMAL(10,2),(select SUM(datediff(n,BKD_ADATE_START, BKD_ADATE_ENDS)) FROM F_BOOK_DATES with (NOLOCK)
INNER JOIN F_BOOK_HEADER with (NOLOCK) ON BKD_FKEY_BK_SEQ = BK_SEQ
AND DATEPART(YYYY,BKD_DATE_START) = DATEPART(YYYY,GETDATE())
AND DATEPART(MM,BKD_DATE_START) = DATEPART(MM,GETDATE())
AND DATEPART(DD,BKD_DATE_START) = DATEPART(DD,GETDATE())
AND BK_STATUS NOT IN ('CX','TP')
AND F_BOOK_HEADER.Deleted <> 1
AND F_BOOK_DATES.Deleted <> 1)),0)
/
sum(case when AllDayBooking = 1 then 1400 else
datediff(n,LO_TIME_START, LO_TIME_END) end) * 100),0),2) as PercentUtilised
from FAREALO with (NOLOCK)
inner join F_LO_TYPE with (NOLOCK) on LO_FKEY_LOT_SEQ = LOT_SEQ
where LO_BK_LOCATION = 1
and LOT_CBS = 1
Anybody see what I've missed?
Thanks
J
Here's the altered query which runs fine, after adding the MIN function:
select
round(COALESCE(
(COALESCE(MIN((select SUM((BKD_ADATE_ENDS - BKD_ADATE_START) * (60 * 24)) FROM F_BOOK_DATES
INNER JOIN F_BOOK_HEADER ON BKD_FKEY_BK_SEQ = BK_SEQ
and EXTRACT (YEAR from BKD_DATE_START ) = EXTRACT (YEAR from SYSDATE )
and EXTRACT (MONTH from BKD_DATE_START ) = EXTRACT (MONTH from SYSDATE )
and EXTRACT (DAY from BKD_DATE_START ) = EXTRACT (DAY from SYSDATE )
AND BK_STATUS NOT IN ('CX','TP')
AND F_BOOK_HEADER.Deleted <> 1
AND F_BOOK_DATES.Deleted <> 1)),9999999999.99,0)
/sum(case when AllDayBooking = 1 then 1400 else
((LO_TIME_END - LO_TIME_START) * (60 * 24)) end) * 100),0),2) as "PercentUtilised"
from FAREALO
inner join F_LO_TYPE on LO_FKEY_LOT_SEQ = LOT_SEQ
where LO_BK_LOCATION = 1
and LOT_CBS = 1;

Select x times for each row without union

Is it possible to write union select queries like the following more succintly?
select
id,
1,
(1 + #defCapUp) * (p.Value + p.Premium),
getdate()
from Products p
union
select
id,
1,
(1 - #defCapDown) * (p.Value - p.Premium),
getdate()
from Products p
union
select
id,
case when p.Paydate > getdate() then 1 else 0 end,
(1 - #defCapUp) * (p.Value - p.Premium),
#nextYear
from Products p
union
select
id,
case when p.Paydate > getdate() then 1 else 0 end,
(1 + #defCapDown) * (p.Value + p.Premium),
#nextYear
from Products p
The statement selects four rows for each row in the Products table. The only thing varying is the formula used to calculate the values for column two and tree. I think there should be a way in sql to write the above without so much ugly code duplication. If only functions were first class objects and sql allowed lambda expressions...
Richard's solution down below is perfect, works very well for the example provided. But I had two typos in the orignal example which makes the problem somewhat tougher:
select
id,
1,
(1 + #defCapUp) * (p.Value + p.Premium),
getdate()
from Products p
union
select
id,
1,
(1 - #defCapDown) * (p.Value - p.Payout),
getdate()
from Products p
union
select
id,
case when p.Paydate > getdate() then 1 else 0 end,
(1 - #defCapUp) * (p.Value - p.Premium),
#nextYear
from Products p
union
select
id,
case when p.Paydate <= getdate() then 1 else 0 end,
(1 + #defCapDown) * (p.Value + p.Payout),
#nextYear
from Products p
The big problem is the case expression in which the comparison operator differs. My problem is that it is very hard to "neatly" handle those cases. What if there were a third case where the comparison was p.Paydate = getdate() for example?
(Not sure how lambda expressions would have helped you)
select
id,
case when p.Paydate > X.CompareDate then 1 else 0 end,
(1 + Cap) * (p.Value + ModF * p.Premium),
#nextYear
from Products p
cross join (
select #defCapUp Cap, Cast(0 as datetime) CompareDate, 1 Modf union all
select -#defCapDown, 0, -1 union all
select -#defCapUp, GETDATE(), -1 union all
select #defCapDown, GETDATE(), 1
) X
BTW, you should have been using UNION ALL, not UNION.
If the order doesn't matter, you could use WHERE.
SELECT id, field2, field3, field4
FROM Products p
WHERE (
field4 = getdate() AND field2=1 AND
(
field3=(1 + #defCapUp) * (p.Value + p.Premium) OR
field3=(1 - #defCapDown) * (p.Value - p.Premium)
)
)
OR
(
field4=#nextYear AND field2=(case when p.Paydate > getdate() then 1 else 0 end) AND
(
field3=(1 - #defCapUp) * (p.Value - p.Premium) OR
field3=(1 + #defCapDown) * (p.Value + p.Premium)
)
)