How to combine two different query into one table? - sql

I have 2 different query.Is it possible combine these query ? If it is possible how can I do it ?
Query 1 : http://pastebin.com/pAdP9Yub
SELECT LatofTruck=Lat, LngofTruck=Lng, SpeedofTruck=Speed
FROM TruckLocation
JOIN Truck AS Trucks ON Trucks.OID=TruckLocation.TruckID
WHERE TruckLocation.OID
IN (
SELECT MAX(TruckLocation_1.OID) AS OID
FROM TruckLocation AS TruckLocation_1
JOIN Truck ON TruckLocation_1.TruckID = Truck.OID GROUP BY
TruckLocation_1.TruckID
)
ORDER BY TruckLocation.ReadTime DESC
Query 2 :http://pastebin.com/Esx36JHU
SELECT a_TankLevel1,a_TankLevel2,d_AlertStation,StateOutAlarm,StateOutValve,
SensorDataPackage.DeviceID,Branch.FirmName,LatofDealer=Branch.Lat,
LngofDealer=Branch.Lng,Branch.City,Branch.FirmPhone,Branch.DealerAdmin
FROM SensorDataPackage
JOIN Dealer AS Branch ON Branch.DeviceID=SensorDataPackage.DeviceID
WHERE SensorDataPackage.OID
IN (
SELECT MAX (SensorDataPackage.OID)
FROM SensorDataPackage
WHERE SensorDataPackage.readTime > DATEADD(DAY,-100,GETDATE())
GROUP BY SensorDataPackage.DeviceID
)
ORDER BY SensorDataPackage.readTime DESC

You can use UNION ALL for this but you have to make sure you have the same number of columns and column types. So:
select a, b, c, from x
union all
select p, q, r, s, t from y
doesn't work but
select a, b, p, q, t from x
union all
select a, b, p, q, t from y
will work.
So in your case, you'd get something like this:
-- First query.
SELECT Latitude = Lat
, Longitude = Lng
, SpeedofTruck = Speed
, a_TankLevel1 = null
, a_TankLevel2 = null
, d_AlertStation = null
, StateOutAlarm = null
, StateOutValve = null
, DeviceID = null
, FirmName = null
, City = null
, FirmPhone = null
, DealerAdmin = null
FROM TruckLocation
JOIN Truck AS Trucks ON Trucks.OID=TruckLocation.TruckID
WHERE TruckLocation.OID
IN (
SELECT MAX(TruckLocation_1.OID) AS OID
FROM TruckLocation AS TruckLocation_1
JOIN Truck ON TruckLocation_1.TruckID = Truck.OID GROUP BY
TruckLocation_1.TruckID
)
UNION ALL
-- Second query.
SELECT Latitude = Branch.Lat
, Longitude = Branch.Lng
, SpeedofTruck = null
, a_TankLevel1
, a_TankLevel2
, d_AlertStation
, StateOutAlarm
, StateOutValve
, SensorDataPackage.DeviceID
, Branch.FirmName
, Branch.City
, Branch.FirmPhone
, Branch.DealerAdmin
FROM SensorDataPackage
JOIN Dealer AS Branch ON Branch.DeviceID=SensorDataPackage.DeviceID
WHERE SensorDataPackage.OID
IN (
SELECT MAX (SensorDataPackage.OID)
FROM SensorDataPackage
WHERE SensorDataPackage.readTime > DATEADD(DAY,-100,GETDATE())
GROUP BY SensorDataPackage.DeviceID
)
This will give you a result set that has latitude and longitude for both trucks and branches.

Hi Common Table Expression would help you in such cases. If you have some common factor between both the tables, you can bring them into a same query and get data in to single query. I have used this feature several times. View is also a very good alternative solution for this.

Related

check for a column if it is null , over a previous term with some conditions

I have lets say two terms term A (previous)and term B (current) , i need to check if pol_cancl_date is null or not in term A , there is a transaction_sequence_number , i need to see if the pol_cncl_date is existing in the greatest A.transaction_sequence_number and if greatest( A.transaction_sequence_number ) is the greated when compared to all B.transaction_sequence_number numbers , if it is then i would want to check for pol_cancl_dates's existence and apply a logic
WITH x AS (
SELECT * FROM (
SELECT
pol_num
,term_start_dt
,term_end_dt,pol_cancel_dt
,trans_seq_num
,future_cancel_dt
,DENSE_RANK() OVER (PARTITION BY pol_num ORDER BY term_end_dt DESC) AS flag
FROM `gcp-ent-datalake-preprod.trns_prop_pol_hs_horison.prop_cost`
--WHERE pol_num IN ('30766675','33896642')
-- pol_num = '33288489'
ORDER BY term_start_dt, term_end_dt DESC
)
)
SELECT
*
,CASE
WHEN prior_pol_cancel_dt IS NOT NULL AND current_trans_seq_num < prior_trans_seq_num THEN prior_pol_cancel_dt
ELSE current_pol_cancel_dt
END apply_cancelled_renewal_dt
FROM (
SELECT
MAX(a.pol_num) AS current_pol_num
,MAX(a.term_start_dt) AS current_term_start_dt
,a.term_end_dt AS current_term_ent_dt
,MAX(a.pol_cancel_dt) AS current_pol_cancel_dt
,MAX(a.trans_seq_num) AS current_trans_seq_num
,MAX(a.future_cancel_dt) AS current_future_cancel_dt
,MAX(a.flag) AS current_flag
,MAX(b.pol_num) AS prior_pol_num
,MAX(b.term_start_dt) AS prior_term_start_dt
,b.term_end_dt AS prior_term_end_dt
,MAX(b.pol_cancel_dt) AS prior_pol_cancel_dt
,MAX(b.trans_seq_num) AS prior_trans_seq_num
,MAX(b.future_cancel_dt) AS prior_future_cancel_dt
,MAX(b.flag) AS prior_flag
FROM (
SELECT * FROM x WHERE flag=1) a
INNER JOIN(
SELECT * FROM x WHERE flag = 2 ) b
ON a.pol_num = b.pol_num AND a.flag = b.flag - 1
WHERE a.pol_cancel_dt IS NOT NULL
AND b.pol_cancel_dt IS NOT NULL
AND greatest(a.trans_seq_num) < b.trans_seq_num
-- AND a.trans_seq_num = GREATEST(a.trans_seq_num)
-- AND b.trans_seq_num = GREATEST(b.trans_seq_num)
GROUP BY a.term_end_dt, b.term_end_dt
)
--WHERE a.term_start_dt < b.term_start_dt
--if prior term GREATEST (trans_sewq num
this logic is still not giving me some results , one thing is that trans_seq_num doesn't necessarily have to be one less

db2 query for multiple cases

I have three queries below (identical except for the first line of the WHERE clauses) that all work perfectly in my script. The first one queries orders for a customer, the 2nd for all orders assigned to a representative and the third are all orders period, across the whole company.
Again, they all work given their respective variables (all variables come from the same page) but I'm trying to fill columns on a table for all 3 cases.
Is there a way I can combine these and create one query that gives me the same values for each respective clause?
So, I would expect all 6 columns returned for one query. This is running on db2 so I don't know the best way to proceed but could I create a larger CASE based query?
//query on orders for this customer
SELECT
count(*) as sales_180Cust,
180/count(*) as velocityCust
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE g.cust = $customer
AND g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
AND i.aciso between current_Date - 180 DAY AND current_Date;
//orders belonging to representative
SELECT
count(*) as sales_180Rep,
180/count(*) as velocityRep
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE g.rep = $rep
AND g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
AND i.aciso between current_Date - 180 DAY AND current_Date;
//query across ALL orders
SELECT
count(*) as sales_180Company,
180/count(*) as velocityCompany
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
AND i.aciso between current_Date - 180 DAY AND current_Date;
This would be another way to do this
WITH CTE AS (
SELECT g.cust
, g.rep
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE
g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
)
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
, 'cust' as query
FROM CTE
WHERE cust = $customer
UNION ALL
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
, 'rep' as query
FROM CTE
WHERE rep = $rep
UNION ALL
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
, 'all' as query
FROM CTE
which returns e.g.
SALES_180COMPANY VELOCITYCOMPANY QUERY
---------------- --------------- -----
3 60 cust
2 90 rep
5 36 all
select q1.*, q2.*, q3.*
from
(select count(*) as sales_180Cust, 180/count(*) as velocityCust from table(values 1) t(i)) q1
, (select count(*) as sales_180Rep, 180/count(*) as velocityRep from table(values 1, 2) t(i)) q2
, (select count(*) as sales_180Company, 180/count(*) as velocityCompany from table(values 1, 2, 3) t(i)) q3
I amended your FROM and WERE clauses to show the idea.
Lots of ways to do this. UNION would be an obvious way. GROUPING SETs is a bit more clever.
create table orders(date1 int, rep int, cust int,frm int, cvr int, clr int, aci int);
create table dates(acyyyymmdd int, aciso date);
create variable $frm int default 1;
create variable $cvr int default 1;
create variable $clr int default 1;
create variable $customer int default 1;
create variable $rep int default 1;
insert into orders values (1,0,0,1,1,1,1), (1,1,1,1,1,1,1), (1,2,1,1,1,1,1), (1,3,1,1,1,1,1), (1,1,2,1,1,1,1);
insert into dates values (1, current date);
then this
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
, g.cust
, g.rep
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE
g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
AND i.aciso between current_Date - 180 DAY AND current_Date
GROUP BY GROUPING SETS ( (), (cust), (rep) )
HAVING (cust = $customer AND rep is null)
OR (cust is null AND rep = $rep)
OR (cust is null AND rep is null)
gives this
SALES_180COMPANY VELOCITYCOMPANY CUST REP
---------------- --------------- ---- ----
5 36 NULL NULL
3 60 1 NULL
2 90 NULL 1
Or this... there are often a multitude of ways to do things in SQL
WITH CTE AS (
SELECT g.cust
, g.rep
FROM orders g
inner join dates i
on g.date1 = i.acyyyymmdd
WHERE
g.frm = $frm
AND g.cvr = $cvr
AND g.clr = $clr
) , CUST AS (
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
FROM CTE
WHERE cust = $customer
) , REP AS (
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
FROM CTE
WHERE rep = $rep
) , ALL AS (
SELECT
count(*) as sales_180Company
, 180/count(*) as velocityCompany
FROM CTE
)
SELECT * FROM CUST, REP, ALL
retuning
SALES_180COMPANY VELOCITYCOMPANY SALES_180COMPANY VELOCITYCOMPANY SALES_180COMPANY VELOCITYCOMPANY
---------------- --------------- ---------------- --------------- ---------------- ---------------
3 60 2 90 5 36

SQL Server Select one column by aggregate but not another

Not good with sql. Forgive me if the question isn't 100% clear. Here is my query
SELECT
MAX(PatientId),
[Date],
[Time],
CASE WHEN MAX(CAST(HealthScoreSkipped as INT)) = 1
THEN '--'
ELSE MAX(DailyHealthScore)
END DailyHealthScore,
ProtocolGroupName,
MAX(BloodPressure) BloodPressure,
MAX(SystolicAlert) SystolicAlert,
MAX(DiastolicAlert) DiastolicAlert,
MAX(BloodPressureSkipped) BloodPressureSkipped,
MAX(Pulse) Pulse,
MAX(PulseAlert) PulseAlert,
MAX(PulseSkipped) PulseSkipped,
MAX(BloodSugar) BloodSugar,
MAX(BloodSugarAlert) BloodSugarAlert,
MAX(BloodSugarSkipped) BloodSugarSkipped,
MAX(Steps) Steps,
MAX(StepsAlert) StepsAlert,
MAX(StepsSkipped) StepsSkipped,
MAX(O2) O2,
MAX(O2Alert) O2Alert,
MAX(O2Skipped) O2Skipped,
MAX(Weight) Weight,
MAX(WeightAlert) WeightAlert,
#BaselineWeight AS BaselineWeight,
MAX(WeightSkipped) WeightSkipped,
MAX(Temperature) Temperature,
MAX(TemperatureAlert) TemperatureAlert,
MAX(TemperatureUnit) TemperatureUnit,
MAX(TemperatureSkipped) TemperatureSkipped,
MAX(PEF) PEF,
MAX(PEFAlert) PEFAlert,
MAX(PEFSkipped) PEFSkipped,
MAX(FEV1) FEV1,
MAX(FEV1Alert) FEV1Alert,
MAX(FEV1Skipped) FEV1Skipped,
MAX(FEVRatio) FEVRatio,
MAX(FEVRatioAlert) FEVRatioAlert,
MAX(FEVRatioSkipped) FEVRatioSkipped,
#SpiroEnabled SpiroEnabled
FROM #bioAndScores
GROUP BY PatientId, Date, Time, ProtocolGroupName
The problem here is on the lines
MAX(Steps) Steps,
MAX(StepsAlert) StepsAlert
I want to select the max Steps but the stepalert value that goes with that row not the max of the stepAlert.
You can create a sub query in the select statement to get the steps alert that corresponds to your step.
something along the lines of the below (note that I'm not sure why you are grouping by patientId, if you are taking the max(patientId) if you do want to group by patient id, the where clause of the sub query should also match on patient Id
SELECT
MAX(bas.PatientId),
bas.[Date],
bas.[Time],
bas.ProtocolGroupName,
.
.
.
MAX(bas.Steps) Steps,
--sub query to get the StepsAlert that corresponds to max steps
(SELECT
StepsAlert
FROM
#bioAndScores subBas
WHERE
--This is the important part of finding the match for Max Steps
MAX(bas.Steps) = subBas.Steps AND
--commented out because the MAX(PatientId) was ambiguous
--bas.PatientId = subBas.PatientId AND
bas.[Date] = subBas.[Date] AND
bas.[Time] = subBas.[Time] AND
bas.ProtocolGroupName = subBas.ProtocolGroupName) as StepsAlert
FROM
#bioAndScores as bas
GROUP BY
--PatientId,
bas.Date,
bas.Time,
bas.ProtocolGroupName
Remove the MAX() function from StepAlerts and add StepAlerts to your GROUP BY clause.
MAX(Steps) AS Steps,
StepsAlert AS StepsAlert
And in your GROUP BY:
GROUP BY PatientId, Date, Time, ProtocolGroupName, StepAlerts
Just add StepsAlert column to the group by clause and remove the MAX aggregate function.
GROUP BY PatientId, Date, Time, ProtocolGroupName,StepsAlert
I would suggest you go through this to better understand about how group by works.
You can do this using apply() to select the set of values that correspond to the highest Steps at the earliest StepsAlert like so:
select
PatientId
, [Date]
, [Time]
, DailyHealthScore = case
when MAX(CAST(HealthScoreSkipped as int))= 1 then '--'
else MAX(DailyHealthScore)
end
, ProtocolGroupName
, BloodPressure = MAX(BloodPressure)
, SystolicAlert = MAX(SystolicAlert)
, DiastolicAlert = MAX(DiastolicAlert)
, BloodPressureSkipped= MAX(BloodPressureSkipped)
, Pulse = MAX(Pulse)
, PulseAlert = MAX(PulseAlert)
, PulseSkipped = MAX(PulseSkipped)
, BloodSugar = MAX(BloodSugar)
, BloodSugarAlert = MAX(BloodSugarAlert)
, BloodSugarSkipped = MAX(BloodSugarSkipped)
, Steps = x.Steps
, StepsAlert = x.StepsAlert
, StepsSkipped = MAX(StepsSkipped)
, O2 = MAX(O2)
, O2Alert = MAX(O2Alert)
, O2Skipped = MAX(O2Skipped)
, Weight = MAX(Weight)
, WeightAlert = MAX(WeightAlert)
, BaselineWeight = #BaselineWeight
, WeightSkipped = MAX(WeightSkipped)
, Temperature = MAX(Temperature)
, TemperatureAlert = MAX(TemperatureAlert)
, TemperatureUnit = MAX(TemperatureUnit)
, TemperatureSkipped = MAX(TemperatureSkipped)
, PEF = MAX(PEF)
, PEFAlert = MAX(PEFAlert)
, PEFSkipped = MAX(PEFSkipped)
, FEV1 = MAX(FEV1)
, FEV1Alert = MAX(FEV1Alert)
, FEV1Skipped = MAX(FEV1Skipped)
, FEVRatio = MAX(FEVRatio)
, FEVRatioAlert = MAX(FEVRatioAlert)
, FEVRatioSkipped = MAX(FEVRatioSkipped)
, SpiroEnabled = #SpiroEnabled
from #bioAndScores b
cross apply (
select top 1
i.Steps
, i.StepsAlert
from #bioAndScores i
where b.PatientId = i.PatientId
and b.[Date] = i.[Date]
and b.[Time] = i.[Time]
and b.ProtocolGroupName = i.ProtocolGroupName
order by i.Steps desc, i.StepsAlert asc
) x
group by
PatientId
, date
, time
, ProtocolGroupName

Pivoting a table-valued function

I have a TVF that returns two columns: 'measure' (a name) and 'score', a numeric score for that measure:
dbo.ScoringFunction(param1, param2, ..., paramN)
Measure Score
------- -----
measure1 10
measure2 5
... ...
measureN 15
I'm running this function against a large number of rows that contain its parameters:
Name Param1 Param2 ... ParamN
---- ------ ------ ------
Daniel 12 5 6
etc.
I am trying to find a way to display the measures and their scores next to the parameters that determine those scores:
Name Param1 Param2 ... ParamN measure1 measure2 ... measureN
---- ------ ------ ------ -------- -------- --------
Daniel 12 5 6 10 5 15
etc.
So far I have tried using a pivot table, but it's tricky since the data being pivoted is contained in a TVF rather than a static table. I've also tried using CROSS APPLY, but once I have the data (measures & scores), I'm still unable to pivot it into a nicely formatted row.
If anybody has any ideas, they would be much appreciated!
Without changing too much (hopefully), I would do the pivoting in the function, then use the function in CROSS APPLY and pull the columns. So if your function is something like this:
CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (
SELECT Measure, Score
FROM …
)
then I would rewrite it like this:
CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (
WITH originalSelect AS (
SELECT Measure, Score
FROM …
)
SELECT
measure1,
measure2,
…
FROM originalSelect
PIVOT (
MAX(Score) FOR Measure IN (measure1, measure2, …)
) p
)
and use it in the final query like this:
SELECT
t.Name,
t.param1,
t.param2,
…
x.measure1,
x.measure2,
…
FROM atable t
CROSS APPLY dbo.ScoringFunction (t.param1, t.param2, …) x
If you make a function that looks like this:
CREATE FUNCTION [dbo].[fGetSpecificMeasures]
(
#HeightScore INT
, #WeightScore INT
, #TvScore INT
)
RETURNS TABLE
RETURN
(
SELECT
Final.Height AS tv_height_score
, Final.[Weight] AS tv_weight_score
, Final.TV AS tv_score
FROM
(
SELECT measure, score FROM ScoringRubric WHERE measure = 'Height' AND #HeightScore BETWEEN bottom_of_range AND top_of_range
UNION ALL
SELECT measure, score FROM ScoringRubric WHERE measure = 'Weight' AND #WeightScore BETWEEN bottom_of_range AND top_of_range
UNION ALL
SELECT measure, score FROM ScoringRubric WHERE measure = 'TV' AND #TvScore BETWEEN bottom_of_range AND top_of_range
) Base
PIVOT
(
MAX(score)
FOR measure
IN
(
[Height]
, [Weight]
, [TV]
)
) Final
);
GO
And one that looks like this:
CREATE FUNCTION [dbo].[fGetMeasureScore]
(
#Measure VARCHAR(50)
, #Value INT
)
RETURNS TABLE
RETURN
(
SELECT
score
FROM ScoringRubric
WHERE measure = #Measure
AND #Value BETWEEN bottom_of_range AND top_of_range
);
GO
Then you can get your data with either of the following:
DECLARE #User VARCHAR(50) = 'Daniel'
SELECT
UserProfile.*
, HeightScore.score AS tv_height_score
, WeightScore.score AS tv_weight_score
, TvScore.score AS tv_score
FROM UserProfile
INNER JOIN ScoringRubric HeightScore
ON HeightScore.measure = 'Height'
AND UserProfile.height BETWEEN HeightScore.bottom_of_range AND HeightScore.top_of_range
INNER JOIN ScoringRubric WeightScore
ON WeightScore.measure = 'Weight'
AND UserProfile.[weight] BETWEEN WeightScore.bottom_of_range AND WeightScore.top_of_range
INNER JOIN ScoringRubric TvScore
ON TvScore.measure = 'TV'
AND UserProfile.TV BETWEEN TvScore.bottom_of_range AND TvScore.top_of_range
WHERE UserProfile.name = #User
SELECT
*
FROM UserProfile
CROSS APPLY dbo.fGetSpecificMeasures(height, [weight], TV)
WHERE name = #User
SELECT
UP.*
, HeightScore.score AS tv_height_score
, WeightScore.score AS tv_weight_score
, TvScore.score AS tv_score
FROM UserProfile UP
CROSS APPLY fGetMeasureScore('Height', UP.height) HeightScore
CROSS APPLY fGetMeasureScore('Weight', UP.[weight]) WeightScore
CROSS APPLY fGetMeasureScore('TV', UP.TV) TvScore
WHERE UP.name = #User
I don't really know which one you'll find most appropriate for your uses. Let me know if you have questions.
As for your original question, if this were the function:
CREATE FUNCTION [dbo].[fGetMeasureScoresOriginal]
(
#HeightScore INT
, #WeightScore INT
, #TvScore INT
)
RETURNS TABLE
RETURN
(
SELECT measure, score FROM ScoringRubric WHERE measure = 'Height' AND #HeightScore BETWEEN bottom_of_range AND top_of_range
UNION ALL
SELECT measure, score FROM ScoringRubric WHERE measure = 'Weight' AND #WeightScore BETWEEN bottom_of_range AND top_of_range
UNION ALL
SELECT measure, score FROM ScoringRubric WHERE measure = 'TV' AND #TvScore BETWEEN bottom_of_range AND top_of_range
)
GO
Then you could write the query and pivot like so:
SELECT
Final.name
, Final.OriginalHeight AS height
, Final.OriginalWeight AS [weight]
, Final.OriginalTv AS TV
, Final.Height AS tv_height_score
, Final.[Weight] AS tv_weight_score
, Final.TV AS tv_score
FROM
(
SELECT
UP.name
, UP.height AS OriginalHeight
, UP.[weight] AS OriginalWeight
, UP.TV AS OriginalTv
, Measures.measure
, Measures.score
FROM UserProfile UP
CROSS APPLY dbo.fGetMeasureScoresOriginal(UP.height, UP.[weight], UP.TV) Measures
WHERE UP.name = #User
) Base
PIVOT
(
MAX(score)
FOR measure
IN
(
[Height]
, [Weight]
, [TV]
)
) Final
EDIT: Just realized I didn't answer the original question. Adding that now.
If you want to pivot a static set of rows you can use the technique as I described here.
Reposting the example, so it will be different tables, but the technique can be applied in your case as well I think.
SELECT
Customers.CustID,
MAX(CASE WHEN CF.FieldID = 1 THEN CF.FieldValue ELSE NULL END) AS Field1,
MAX(CASE WHEN CF.FieldID = 2 THEN CF.FieldValue ELSE NULL END) AS Field2,
MAX(CASE WHEN CF.FieldID = 3 THEN CF.FieldValue ELSE NULL END) AS Field3,
MAX(CASE WHEN CF.FieldID = 4 THEN CF.FieldValue ELSE NULL END) AS Field4
-- Add more...
FROM Customers
LEFT OUTER JOIN CustomFields CF
ON CF.ID = Customers.CustID
WHERE Customers.CustName like 'C%'
GROUP BY Customers.CustID

speed up SQL Query

I have a query which is taking some serious time to execute on anything older than the past, say, hours worth of data. This is going to create a view which will be used for datamining, so the expectations are that it would be able to search back weeks or months of data and return in a reasonable amount of time (even a couple minutes is fine... I ran for a date range of 10/3/2011 12:00pm to 10/3/2011 1:00pm and it took 44 minutes!)
The problem is with the two LEFT OUTER JOINs in the bottom. When I take those out, it can run in about 10 seconds. However, those are the bread and butter of this query.
This is all coming from one table. The ONLY thing this query returns differently than the original table is the column xweb_range. xweb_range is a calculated field column (range) which will only use the values from [LO,LC,RO,RC]_Avg where their corresponding [LO,LC,RO,RC]_Sensor_Alarm = 0 (do not include in range calculation if sensor alarm = 1)
WITH Alarm (sub_id,
LO_Avg, LO_Sensor_Alarm, LC_Avg, LC_Sensor_Alarm, RO_Avg, RO_Sensor_Alarm, RC_Avg, RC_Sensor_Alarm) AS (
SELECT sub_id, LO_Avg, LO_Sensor_Alarm, LC_Avg, LC_Sensor_Alarm, RO_Avg, RO_Sensor_Alarm, RC_Avg, RC_Sensor_Alarm
FROM dbo.some_table
where sub_id <> '0'
)
, AddRowNumbers AS (
SELECT rowNumber = ROW_NUMBER() OVER (ORDER BY LO_Avg)
, sub_id
, LO_Avg, LO_Sensor_Alarm
, LC_Avg, LC_Sensor_Alarm
, RO_Avg, RO_Sensor_Alarm
, RC_Avg, RC_Sensor_Alarm
FROM Alarm
)
, UnPivotColumns AS (
SELECT rowNumber, value = LO_Avg FROM AddRowNumbers WHERE LO_Sensor_Alarm = 0
UNION ALL SELECT rowNumber, LC_Avg FROM AddRowNumbers WHERE LC_Sensor_Alarm = 0
UNION ALL SELECT rowNumber, RO_Avg FROM AddRowNumbers WHERE RO_Sensor_Alarm = 0
UNION ALL SELECT rowNumber, RC_Avg FROM AddRowNumbers WHERE RC_Sensor_Alarm = 0
)
SELECT rowNumber.sub_id
, cds.equipment_id
, cds.read_time
, cds.LC_Avg
, cds.LC_Dev
, cds.LC_Ref_Gap
, cds.LC_Sensor_Alarm
, cds.LO_Avg
, cds.LO_Dev
, cds.LO_Ref_Gap
, cds.LO_Sensor_Alarm
, cds.RC_Avg
, cds.RC_Dev
, cds.RC_Ref_Gap
, cds.RC_Sensor_Alarm
, cds.RO_Avg
, cds.RO_Dev
, cds.RO_Ref_Gap
, cds.RO_Sensor_Alarm
, COALESCE(range1.range, range2.range) AS xweb_range
FROM AddRowNumbers rowNumber
LEFT OUTER JOIN (SELECT rowNumber, range = MAX(value) - MIN(value) FROM UnPivotColumns GROUP BY rowNumber HAVING COUNT(*) > 1) range1 ON range1.rowNumber = rowNumber.rowNumber
LEFT OUTER JOIN (SELECT rowNumber, range = AVG(value) FROM UnPivotColumns GROUP BY rowNumber HAVING COUNT(*) = 1) range2 ON range2.rowNumber = rowNumber.rowNumber
INNER JOIN dbo.some_table cds
ON rowNumber.sub_id = cds.sub_id
It's difficult to understand exactly what your query is trying to do without knowing the domain. However, it seems to me like your query is simply trying to find, for each row in dbo.some_table where sub_id is not 0, the range of the following columns in the record (or, if only one matches, that single value):
LO_AVG when LO_SENSOR_ALARM=0
LC_AVG when LC_SENSOR_ALARM=0
RO_AVG when RO_SENSOR_ALARM=0
RC_AVG when RC_SENSOR_ALARM=0
You constructed this query assigning each row a sequential row number, unpivoted the _AVG columns along with their row number, computed the range aggregate grouping by row number and then joining back to the original records by row number. CTEs don't materialize results (nor are they indexed, as discussed in the comments). So each reference to AddRowNumbers is expensive, because ROW_NUMBER() OVER (ORDER BY LO_Avg) is a sort.
Instead of cutting this table up just to join it back together by row number, why not do something like:
SELECT cds.sub_id
, cds.equipment_id
, cds.read_time
, cds.LC_Avg
, cds.LC_Dev
, cds.LC_Ref_Gap
, cds.LC_Sensor_Alarm
, cds.LO_Avg
, cds.LO_Dev
, cds.LO_Ref_Gap
, cds.LO_Sensor_Alarm
, cds.RC_Avg
, cds.RC_Dev
, cds.RC_Ref_Gap
, cds.RC_Sensor_Alarm
, cds.RO_Avg
, cds.RO_Dev
, cds.RO_Ref_Gap
, cds.RO_Sensor_Alarm
--if the COUNT is 0, xweb_range will be null (since MAX will be null), if it's 1, then use MAX, else use MAX - MIN (as per your example)
, (CASE WHEN stats.[Count] < 2 THEN stats.[MAX] ELSE stats.[MAX] - stats.[MIN] END) xweb_range
FROM dbo.some_table cds
--cross join on the following table derived from values in cds - it will always contain 1 record per row of cds
CROSS APPLY
(
SELECT COUNT(*), MIN(Value), MAX(Value)
FROM
(
--construct a table using the column values from cds we wish to aggregate
VALUES (LO_AVG, LO_SENSOR_ALARM),
(LC_AVG, LC_SENSOR_ALARM),
(RO_AVG, RO_SENSORALARM),
(RC_AVG, RC_SENSOR_ALARM)
) x (Value, Sensor_Alarm) --give a name to the columns for _AVG and _ALARM
WHERE Sensor_Alarm = 0 --filter our constructed table where _ALARM=0
) stats([Count], [Min], [Max]) --give our derived table and its columns some names
WHERE cds.sub_id <> '0' --this is a filter carried over from the first CTE in your example