SQL-using CASE in SELECT returns only first case value - sql

I'm using the following query:
SELECT Policy_type_ID,Policy_Value Value,CASE Policy_Value WHEN max(Policy_Value) THEN 'Highest' WHEN min(Policy_Value) THEN 'Lowest' END AS Range
FROM Policy_Types
GROUP BY Policy_type_ID,Policy_Value
HAVING ((Policy_Value IN (SELECT max(Policy_Value)
FROM Policy_Types)) OR (Policy_Value IN(SELECT min(Policy_Value)
FROM Policy_Types)));
But the result has only one value 'Highest' in the column 'Range'.Its only regarding the first case,whichever it maybe, and ignoring the rest.
Policy_type_ID Value Range
501180 990000 Highest
690002 10 Highest
690006 10 Highest
690007 10 Highest
I've no idea where I'm going wrong. Its just that CASE statement that is the problem....any help??

The problem is that your MIN and MAX functions are being calculated within the GROUP BY groups, not across the entire table. You need to calculate them in a separate subquery that doesn't have GROUP BY.
SELECT DISTINCT Policy_type_ID, Policy_Value,
CASE Policy_Value
WHEN MaxPolicy THEN 'Highest'
ELSE 'Lowest'
END Range
FROM Policy_Types
JOIN (SELECT MIN(Policy_Value) MinPolicy, MAX(Policy_Value) MaxPolicy) MinMax
HAVING Policy_Value IN (MinPolicy, MaxPolicy)

Related

Using a subquery in a where clause to find the second smallest value

I'm trying to find the second smallest value for a list to put it into SSRS as a way to highlight that value. This issue is there are multiple minimum values for a given element. The data is presented such that there is an overarching group A that encompasses smaller groups B and I am wanting the second smallest value for each of the smaller groups.
I have a query set up right now that uses a subquery in the where clause to exclude the minimum value from the search so that the second smallest value will be considered the new minimum value. This seemed to work but the subquery only rules out the minimum value for the larger A group, which may or may not be the minimum value for each B group. Here is my query:
Select
BPosition,
Min(Value) as SecondMinimum
From Table
Where Value > (Select
Min(Value)
From Table
Where APosition = #AName)
and APosition = #AName
Group By BPosition
I was expecting a list of the second smallest values for each B group, but it is pulling in the smallest value in each B group that is greater than the smallest value of the A group. This is right for the one B group that contains the true smallest value but incorrect for the others.
If you want the second largest value, use dense_rank():
Select distinct BPosition, Value as SecondMinimum
From (select t.*,
dense_rank() over (partition by Aposition, Bposition order by value) as seqnum
from table
) t
where seqnum = 2;

Specifying a column value in an aggregate function vs using a WHERE clause

I have a database people that looks like this:
I wanted to count the occurrences of state='CA'.
My first attempt was:
SELECT COUNT(state='CA')
FROM people
;
this returned 1 row with a value of 1000. So I thought that there were 1000 people from CA in the database.
This turns out to be incorrect. I know that they are 127, which I can verify with the query
SELECT COUNT(*)
FROM people
WHERE state='CA'
;
which returns 1 row with a value of 127.
I understand how the second query works. However, I do not understand what is wrong with the first one. What is it returning?
If you want to see what's going on, run the query:
select state='CA' from people;
You will see that you will get one result for each row in people, with the value 0 or 1 (or True/False). What you've selected is whether state='CA' for each row, and there will be just as many of those results as there are rows.
You can't constrain a COUNT statement within the statement, you have to do that via the WHERE clause as in your second example.
count is not a sum .. your first query is improper because don't return the number of the rows true .. but the total numbers of not null rows true or false
if you want a filter count you must use a where condition (as your second query) otherwise you must use an if or a a select case inside the sum() function eg:
Select sum(case
when state='CA' then 1 else 0
end) as my_result from People;
or if you want count .. use null and not 0min count
Select count(case
when state='CA' then 1 else null
end) as my_result from People;
Try this-:
Select count(case when state='CA' then 1 else null end) as xyz from People;
1st query will work if you use case when in side count,
like below query will returned count of CA
SELECT sum( case when state='CA' then 1 else 0 end)
FROM people
In first query it is assigning the value 'CA' to the column state for all 1000 rows instead of filtering the values. That is what SELECT does. SELECT does not filter the number of returning rows, it modifies the data.
Whereas in WHERE clause the rows are being filtered first then the SELECT clause runs the COUNT function.
There is a sequence for running the query. It starts from FROM then WHERE, GROUP BY, ORDER BY at the end SELECT will run.
To answer the actual question - why do you get 1000? I'm guessing that there are 1000 rows in your database, or at least 1000 where state is not null. Count will return the number of rows where the thing inside the () is not null and as one of your comments says, the part inside your () will return either true or false, neither of which is null, so will count them all. Your second example is of course the right way to do it.

Remove Min and Max in group by

I am trying to get a total hours from a dataset and because you can have the same asset with the same company (company_B) twice at two different times I have this join issue. I know I want the min for company_B gone and the Max for company_B gone because they represent wrong dates being matched. The negative is easy but what about the Max?
I have:
AssetID------StartDate-------FinishDate-------CompanyName----HoursOnSite
22222-------2016-02-12-------2016-02-20-------Company_A--------192
22222-------2016-02-01-------2016-02-09-------Company_B--------208 (keep)
22222-------2016-02-12-------2016-02-09-------Company_B-------(-56) (remove)
22222-------2016-02-01-------2016-02-21-------Company_B--------480 (remove)
22222-------2016-02-12-------2016-02-21-------Company_B--------216 (keep)
55555-------2016-02-18-------2016-02-22-------Company_C--------96
99584-------2016-02-22-------2016-02-25-------Company_D--------63
I think you can do the query for the records with max and min HoursOnSite for company B, and use (not in) or not equal to exclude those records.
If you still have concern, please paste your query.
I'm assuming that there has to be atleast 3 instances of unique assetid - companyname combination for the Max, Min filters to work. You can change it in the final where statement tO suit your requirement
WITH CTE
AS (
SELECT *
,count(CompanyName) OVER (PARTITION BY AssetID,CompanyName) AS a
FROM <TABLE_NAME>
)
SELECT *
FROM CTE
WHERE HoursOnSite NOT IN (
SELECT MAX(HoursOnSite)
FROM <TABLE_NAME>
)
AND gdp NOT IN (
SELECT min(HoursOnSite)
FROM <TABLE_NAME>
)
AND a > 2 --MODIFY AS PER YOUR REQUIREMENT

Comparing 2 values in the Same column

I have a table like following :
Orderserialno SKU Units
1234-6789 2x3 5
1234-6789 4x5 7
1334-8905 4x5 2
1334-8905 6x10 2
I need to get the count of distinct orderserialno where Units are not equal within a orderserialno. There could be more combinations of Sku's in an order than what I have mentioned but the eventual goal is to get those orders where units corresponding to various SKUs (in that order) are not equal.
In the above case I should get answer as 1 as orderserialno 1234-6789 has different units.
Thanks
This is a relatively simple GROUP BY query:
SELECT Orderserialno, Units
FROM MyTable
GROUP BY Orderserialno, Units
HAVING COUNT(1) > 1
This would give you all pairs (Orderserialno, Units). To project out the Units, nest this query inside a DISTINCT, like this:
SELECT DICTINCT(Orderserialno) FROM (
SELECT Orderserialno, Units
FROM MyTable
GROUP BY Orderserialno, Units
HAVING COUNT(1) > 1
)
If you need only the total count of Orderserialnos with multiple units, replace DICTINCT(Orderserialno) with COUNT(DICTINCT Orderserialno).
To get the list of such order numbers, use an aggregation query:
select OrderSerialNo
from t
group by OrderSerialNo
having min(Units) <> max(Units)
This uses a trick to see if the units value changes. You can use count(distinct), but that usually incurs a performance overhead. Instead, just compare the minimum and maximum values. If they are different, then the value is not constant.
To get the count, use this as a subquery:
select count(*)
from (select OrderSerialNo
from t
group by OrderSerialNo
having min(Units) <> max(Units)
) t

Check Sequence in Max Min Values

I have a database table that Stores Maximum and Minimum Price Breaks for a Product.
Does anyone know of the SQL which say if I have a break from one Max to the Min of the next item. E.g. 1-10 12-20 I would like it to return me either the numbers that are missing or at the very least a count or bool if it can detect a break from the Absolute Min and the Absolute Max by going through each range.
SQL Server (MSSQL) 2008
For a database that supports window functions, like Oracle:
SELECT t.*
, CASE LAG(maxq+1, 1, minq) OVER (PARTITION BY prod ORDER BY minq)
WHEN minq
THEN 0
ELSE 1
END AS is_gap
FROM tbl t
;
This will produce is_gap = 1 for a row that forms a gap with the previous row (ordered by minq). If your quantity ranges can overlap, the required logic would need to be provided.
http://sqlfiddle.com/#!4/f609e/4
Something like this, giving max quantities that aren't the overall max for the product and don't have a min quantity following them:
select prev.tbProduct_Id,prev.MaxQuantity
from yourtable prev
left join (select tbProduct_ID, max(MaxQuantity) MaxQuantity from yourtable group by tbProduct_id) maxes
on maxes.tbProduct_ID=prev.tbProduct_Id and maxes.MaxQuantity=prev.MaxQuantity
left join yourtable next
on next.tbProduct_Id=prev.tbProduct_Id and next.MinQuantity=prev.MaxQuantity+1
where maxes.tbProduct_Id is null and next.tbProduct_Id is null;
This would fail on your sample data, though, because it would expect a row with MinQuantity 21, not 20.