Select highest value based off of a different column - sql

I am trying to get the highest value based off of another column.
SELECT DISTINCT
AppDetailVehicleValuation.AppID,
VehicleValuationOption.Description,
MAX (VehicleValuationOptionValueType.Value)
FROM
AppDetailVehicleValuation
INNER JOIN VehicleValuationOption
ON AppDetailVehicleValuation.ValuationID = VehicleValuationOption.ValuationID
INNER JOIN VehicleValuationOptionValueType
ON VehicleValuationOption.ValuationOptionID = VehicleValuationOptionValueType.ValuationOptionID
WHERE
(VehicleValuationOption.IsSelected LIKE '1')
AND (VehicleValuationOption.IsSystemOption LIKE '1')
What I have is this
AppID | Description | Value
999 Beats Audio 425.00
999 Beats Audio 475.00
999 Power Str. 600.00
999 Power Str. 750.00
this is what I need
AppID | Description | Value
999 Beats Audio 475.00
999 Power Str. | 750.00

You are just missing a GROUP BY clause in your query:
SELECT
AppDetailVehicleValuation.AppID,
VehicleValuationOption.Description,
MAX (VehicleValuationOptionValueType.Value)
FROM
AppDetailVehicleValuation
INNER JOIN VehicleValuationOption
ON AppDetailVehicleValuation.ValuationID = VehicleValuationOption.ValuationID
INNER JOIN VehicleValuationOptionValueType
ON VehicleValuationOption.ValuationOptionID = VehicleValuationOptionValueType.ValuationOptionID
WHERE
(VehicleValuationOption.IsSelected LIKE '1')
AND (VehicleValuationOption.IsSystemOption LIKE '1')
GROUP BY AppDetailVehicleValuation.AppID, VehicleValuationOption.Description

You can simply do this:
SELECT
t.AppId,
t.Description,
max(t.Value)
FROM mytable t
GROUP BY t.description, t.AppId

This is too long for a comment.
Glad you found an answer that works with the GROUP BY. I would suggest you start using aliases in your queries. It can quickly and easily turn a wall of text into something fairly easy to see what is going on. You might end with something along these lines.
SELECT advv.AppID
, vvo.Description
, MaxValue = MAX(vvot.Value)
FROM AppDetailVehicleValuation advv
INNER JOIN VehicleValuationOption vvo ON advv.ValuationID = vvo.ValuationID
INNER JOIN VehicleValuationOptionValueType vvot ON vvo.ValuationOptionID = vvot.ValuationOptionID
WHERE vvo.IsSelected = '1'
AND vvo.IsSystemOption = '1'
group by advv.AppID
, vvo.Description

Related

SQL Count with join are returning double results

I have two tables, "event" and "soundType". I am trying to count the number of event with specific soundType.
This is my request :
SELECT Count(*) AS nb
FROM event
INNER JOIN soundtype
ON event.id = soundtype.eventid
WHERE ( soundtype.NAME = 'pop'
OR soundtype.NAME = 'rock' )
AND ( event.partytype = 'wedding'
OR event.partytype = 'Corporate evening'
OR event.partytype = 'birthday' )
Example of tables below:
event Table
id userId partyType
----------------------------
249 30 birthday
250 30 wedding
SoundType Table
id evenId name
-----------------------
1 249 pop
2 249 rock
3 250 pop
The result
nb
---
3
The result i expect
nb
---
2
Thank you for your help
You might find that exists is more efficient than count(distinct):
SELECT COUNT(*) AS nb
FROM event e
WHERE e.partytype IN ('wedding', 'Corporate evening' , 'birthday') AND
EXISTS (SELECT 1
FROM soundtype st
WHERE st.eventid = e.id AND
st.NAME IN ('pop', 'rock')
) ;
Your problem is (presumably) arising because some events have multiple sound types. You just need to match one of them. Multiplying out all the rows just to use COUNT(DISTINCT) is inefficient, when EXISTS (or IN) prevents the duplicates in the first place.
You count all the resulting records. But you need to count different events. So use distinct
SELECT COUNT(distinct event.id) AS nb
FROM event
INNER JOIN soundType ON event.id = soundType.eventId
WHERE soundType.name in('pop', 'rock')
AND event.partyType in('wedding', 'Corporate evening', 'birthday')

Difference Max and Min from Different Dates

I'm going to try to explain this the best I can.
The code below does the following:
Finds a service address from the ServiceLocation table.
Finds a service type (electric or water).
Finds how many days in the past to pull data.
Once it has this, it calculates the "daily usage" by subtracting the max meter read for a day from the minimum meter read for a day.
(MAX(mr.Reading) - MIN(mr.Reading)) AS 'DaytimeUsage'
However, what I'm missing is the max reading from the day prior and the minimum reading from the current day. Mathematically, this should look something like this:
MAX(PriorDayReading) - MIN(ReadDateReading)
Essentially, if it goes back 5 days it should kick out a table that reads as follows:
Service Location | Read Date | Usage |
123 Main St | 4/20/15 | 12 |
123 Main St | 4/19/15 | 8 |
123 Main St | 4/18/15 | 6 |
123 Main St | 4/17/15 | 10 |
123 Main St | 4/16/15 | 11 |
Where "Usage" is the 'DaytimeUsage' + usage that I'm missing (and the question above). For example, 4/18/15 would be the 'DaytimeUsage' in the query below PLUS the the difference between the MAX read from 4/17/15 and the MIN read from 4/18/15.
I'm not sure how to accomplish this or if it is possible.
SELECT
A.ServiceAddress AS 'Service Address',
convert(VARCHAR(10),A.ReadDate,101) AS 'Date',
SUM(A.[DaytimeUsage]) AS 'Usage'
FROM
(
SELECT
sl.location_addr AS 'ServiceAddress',
convert(VARCHAR(10),mr.read_date,101) AS 'ReadDate',
(MAX(mr.Reading) - MIN(mr.Reading)) AS 'DaytimeUsage'
FROM
DimServiceLocation AS sl
INNER JOIN FactBill AS fb ON fb.ServiceLocationKey = sl.ServiceLocationKey
INNER JOIN FactMeterRead as mr ON mr.ServiceLocationKey = sl.ServiceLocationKey
INNER JOIN DimCustomer AS c ON c.CustomerKey = fb.CustomerKey
WHERE
c.class_name = 'Tenant'
AND sl.ServiceLocationKey = #ServiceLocation
AND mr.meter_type = #ServiceType
GROUP BY
sl.location_addr,
convert(VARCHAR(10),
mr.read_date,101)
) A
WHERE A.ReadDate >= GETDATE()-#Days
GROUP BY A.ServiceAddress, convert(VARCHAR(10),A.ReadDate,101)
ORDER BY convert(VARCHAR(10),A.ReadDate,101) DESC
It seems like you could solve this by just calculating the difference between the MAX of yesterday & today, however this is how I would approach it. Join to the same table again for the previous day relative to any given day, and select the Max/Min for that too within your inner query. Also if you place the date in the inner query where clause the data set you return will be quicker & smaller.
SELECT
A.ServiceAddress AS 'Service Address',
convert(VARCHAR(10),A.ReadDate,101) AS 'Date',
SUM(A.[TodayMax]) - SUM(A.[TodayMin]) AS 'Usage',
SUM(A.[TodayMax]) - SUM(A.[YesterdayMax]) AS 'Usage with extra bit you want'
FROM
(
SELECT
sl.location_addr AS 'ServiceAddress',
convert(VARCHAR(10),mr.read_date,101) AS 'ReadDate',
MAX(mrT.Reading) AS 'TodayMax',
MIN(mrT.Reading) AS 'TodayMin',
MAX(mrY.Reading) AS 'YesterdayMax',
MIN(mrY.Reading) AS 'YesterdayMin',
FROM
DimServiceLocation AS sl
INNER JOIN FactBill AS fb ON fb.ServiceLocationKey = sl.ServiceLocationKey
INNER JOIN FactMeterRead as mrT ON mrT.ServiceLocationKey = sl.ServiceLocationKey
INNER JOIN FactMeterRead as mrY ON mrY.ServiceLocationKey = s1.ServiceLocationKey
AND mrY.read_date = mrT.read_date -1)
INNER JOIN DimCustomer AS c ON c.CustomerKey = fb.CustomerKey
WHERE
c.class_name = 'Tenant'
AND sl.ServiceLocationKey = #ServiceLocation
AND mr.meter_type = #ServiceType
AND convert(VARCHAR(10), mrT.read_date,101) >= GETDATE()-#Days
GROUP BY
sl.location_addr,
convert(VARCHAR(10),
mr.read_date,101)
) A
GROUP BY A.ServiceAddress, convert(VARCHAR(10),A.ReadDate,101)
ORDER BY convert(VARCHAR(10),A.ReadDate,101) DESC
You can use the APPLY operator if you are above sql server 2005. Here is a link to the documentation. https://technet.microsoft.com/en-us/library/ms175156(v=sql.105).aspx The APPLY operation comes in two forms OUTER APPLY AND CROSS APPLY - OUTER works like a left join and CROSS works like an inner join. They let you run a query once for each row returned. I setup my own sample of what you were trying to do, here it is and I hope it helps.
http://sqlfiddle.com/#!6/fdb3f/1
CREATE TABLE SequencedValues (
Location varchar(50) NOT NULL,
CalendarDate datetime NOT NULL,
Reading int
)
INSERT INTO SequencedValues (
Location,
CalendarDate,
Reading
)
SELECT
'Address1',
'4/20/2015',
10
UNION SELECT
'Address1',
'4/19/2015',
9
UNION SELECT
'Address1',
'4/19/2015',
20
UNION SELECT
'Address1',
'4/19/2015',
25
UNION SELECT
'Address1',
'4/18/2015',
8
UNION SELECT
'Address1',
'4/17/2015',
7
UNION SELECT
'Address2',
'4/20/2015',
100
UNION SELECT
'Address2',
'4/20/2015',
111
UNION SELECT
'Address2',
'4/19/2015',
50
UNION SELECT
'Address2',
'4/19/2015',
65
SELECT DISTINCT
sv.Location,
sv.CalendarDate,
sv_dayof.MINDayOfReading,
sv_daybefore.MAXDayBeforeReading
FROM SequencedValues sv
OUTER APPLY (
SELECT MIN(sv_dayof_inside.Reading) AS MINDayOfReading
FROM SequencedValues sv_dayof_inside
WHERE sv.Location = sv_dayof_inside.Location
AND sv.CalendarDate = sv_dayof_inside.CalendarDate
) sv_dayof
OUTER APPLY (
SELECT MAX(sv_daybefore_max.Reading) AS MAXDayBeforeReading
FROM SequencedValues sv_daybefore_max
WHERE sv.Location = sv_daybefore_max.Location
AND sv_daybefore_max.CalendarDate IN (
SELECT TOP 1 sv_daybefore_inside.CalendarDate
FROM SequencedValues sv_daybefore_inside
WHERE sv.Location = sv_daybefore_inside.Location
AND sv.CalendarDate > sv_daybefore_inside.CalendarDate
ORDER BY sv_daybefore_inside.CalendarDate DESC
)
) sv_daybefore
ORDER BY
sv.Location,
sv.CalendarDate DESC
I'm not sure I full understood your db structure but I may have a solution so feel free to edit my answer to adapt or correct any mistake.
The idea is to use two aliases for the table FactMeterRead. mrY (Y as yesterday) and mrT (T as Today). And differentiate them with a read_date restriction.
However I didn't understand enough your tables to write a fully functional query. I hope you will get the idea anyway with this example.
SELECT
sl.location_addr AS 'ServiceAddress',
convert(VARCHAR(10),mrT.read_date,101) AS 'ReadDate',
(MAX(mrY.Reading) - MIN(mrT.Reading)) AS 'DaytimeUsage'
FROM
DimServiceLocation AS sl
INNER JOIN FactMeterRead as mrY ON mrY.ServiceLocationKey = sl.ServiceLocationKey
INNER JOIN FactMeterRead as mrT ON mrT.ServiceLocationKey = sl.ServiceLocationKey
WHERE mrY.read_date=DATE_SUB(mrT.read_date,1 DAY)

Show column value of one of left joins

In this Query shows one row if SAL.ID_AMENAZA exists in MFT or MFA schemas, but i want to show MFT.ID_AMENAZA or MFA.ID_AMENAZA (if exist in any table) in the resulting row.
In my actual query i dont require the ID_AMENAZA. How can i show?
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA
FROM AGR_SALVAGUARDAS SAL
LEFT JOIN AGR_MIT_FREC_TIPO MFT
ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = 5043
LEFT JOIN AGR_MIT_FREC_ACT MFA
ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = 5043
WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR
MFA.ID_SALVAGUARDA IS NOT NULL
GROUP BY SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA
I need to obtain a table like this:
ID_SALVAGUARDA | DESCRIPCION | EFICACIA | ID_AMENAZA
5061 | PRE-01 | 100 | 5043
Thank you in advance.
This should work for you:
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA, 5043 as ID_AMENAZA
FROM AGR_SALVAGUARDAS SAL
LEFT JOIN AGR_MIT_FREC_TIPO MFT
ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = 5043
LEFT JOIN AGR_MIT_FREC_ACT MFA
ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = 5043
WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR
MFA.ID_SALVAGUARDA IS NOT NULL
GROUP BY SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA;
because your query guarantees that there is at least one match.
However, the more general solution is:
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA,
cOALESCE(MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA
. . .
The function COALESCE() returns the first value that is not NULL in its list of arguments.
You can coalesce for this.
SELECT
SAL.ID_SALVAGUARDA,
SAL.DESCRIPCION,
SAL.EFICACIA,
COALESCE( MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA
This will return the first ID that is not null.

Get percentages of larger group

The query below is kind of an ugly one so I hope I've got it spaced well enough to make it readable. The query finds the percentage of people that visit a given hospital if they are from a certain area. For instance, if 100 people live in county X and 20 go to hospital A and 80 go to hospital B the query outputs. How the heck is this sort of thing done? Let me know if I need to document the query or whatever I can do to make it clearer.
hospital A 20
hospital B 80
The query below works exactly like I want it to, but it give me thinking: how could this be done for every county in my table?
select hospitalname, round(cast(counts as float)/cast(fayettestrokepop as float)*100,2)as percentSeen
from
(
SELECT tblHospitals.hospitalname, COUNT(tblHospitals.hospitalname) AS counts, tblStateCounties_1.countyName,
(SELECT COUNT(*) AS Expr1
FROM Patient INNER JOIN
tblStateCounties ON Patient.stateCode = tblStateCounties.stateCode AND Patient.countyCode = tblStateCounties.countyCode
WHERE (tblStateCounties.stateCode = '21') AND (tblStateCounties.countyName = 'fayette')) AS fayetteStrokePop
FROM Patient AS Patient_1 INNER JOIN
tblHospitals ON Patient_1.hospitalnpi = tblHospitals.hospitalnpi INNER JOIN
tblStateCounties AS tblStateCounties_1 ON Patient_1.stateCode = tblStateCounties_1.stateCode AND Patient_1.countyCode = tblStateCounties_1.countyCode
WHERE (tblStateCounties_1.stateCode = '21') AND (tblStateCounties_1.countyName = 'fayette')
GROUP BY tblHospitals.hospitalname, tblStateCounties_1.countyName
) as t
order by percentSeen desc
EDIT: sample data
The sample data below is without the outermost query (the as t order by part).
The countsInTheCounty column is the (select count(*)..) part after 'tblStateCounties_1.countyName'
hospitalName hospitalCounts countyName countsInTheCounty
st. james 23 X 300
st. jude 40 X 300
Now with the outer query we would get
st james 0.076 (23/300)
st. jude 0.1333 (40/300)
Here is my guess. You'll have to test against your data or provide proper DDL + sample data.
;WITH totalCounts AS
(
SELECT StateCode, countyCode, COUNT(*) AS totalcount
FROM dbo.Patient GROUP BY StateCode, countyCode
)
SELECT
h.hospitalName,
hospitalCounts = COUNT(p.hospitalnpi),
c.countyName,
countsInTheCounty = tc.totalCount,
percentseen = CONVERT(DECIMAL(5,2), COUNT(p.hospitalnpi)*100.0/tc.totalCount)
FROM
dbo.Patient AS p
INNER JOIN
dbo.tblHospitals AS h
ON p.hospitalnpi = h.hospitalnpi
INNER JOIN
totalCounts AS tc
ON p.StateCode = tc.StateCode
AND p.countyCode = tc.countyCode
INNER JOIN
dbo.tblStateCounties AS c
ON tc.StateCode = c.stateCode
AND tc.countyCode = c.countyCode
GROUP BY
h.hospitalname,
c.countyName,
tc.totalcount
ORDER BY
c.countyName,
percentseen DESC;

SQL to select parent that contains child specific value

I am actually creating a crystal reports v12 (2008) report but can't find the method, using Crystal, to extract the following. I thought if someone might answer in SQL language, I could piece it together.
2 Tables: hbmast, ddmast
SELECT hbmast.custno, hbmast.id, ddmast.name, ddmast.status
WHERE hbmast.custno = ddmast.custno
GROUP BY hbmast.id
pseudo code::show all hbmast values that have ddmast.status = '2'
Sample output:
J0001, 111222, PAUL JONES, 1
111222, PAUL JONES, 2
111222, PAUL JONES, 1
K0001, 555333, PETER KING, 3
555333, PETER KING, 1
I would like to have Paul show on the report with all child records but Peter should not be returned on the report since he has no child records with '2' for ddmast.status field.
Thanks for the help
I think you're looking for this:
select hb.custno, hb.id, dd.name, dd.status from hbmast hb
join ddmast dd on hb.custno = dd.custno
where hb.custno in (
select custno from ddmast
where status = '2'
)
Let me know if this returns your expected result.
The way to achieve this in Crystal would be to have your hb and dd tables then a second alias of the dd table.
So you would filter your dd alias table where status = 2 then join to your hb table and back to your dd table (not the alias). The SQL would end up looking like:
select hb.custno, hb.id, dd.name, dd.status from hbmast hb
inner join ddmast dd on hb.custno = dd.custno
inner join ddmast dd2 on hb.custno = dd2.custno
where dd2.status = '2'
Andomar makes a valid point about duplicate records appearing if there is more than 1 record per group with a status of 2. If that is the case you can either group by primary key and show row information at group footer level OR use a sql expression with a subquery in your selection formula instead of the double join method.
SQL Expression: (select count(*) from ddmast where custno = "hbmast.custno" and status = '2')
Then record selection expert: {%sqlexpression} > 0
And a different way to get the same...
SELECT hb.custno, hb.id, dd.name, dd.status
FROM hbmast hb
INNER join ddmast dd
on hb.custno = dd.custno
INNER JOIN DDMAST2 DD2
on DD2.custNo = HB.custNo
AND DD2.Status='2'