Do not want to include Case alias in group by clause - sql

SELECT SUM (e.quantity) AS quantity
,e.tran_id
,e.SEQUENCE
,MAX (e.veh) AS veh
,a.DATE AS DATE
,a.aloc_sequence
,CASE
WHEN a.currency = 'USD' THEN e.COST
ELSE e.COST * getrate ('ABC'
,a.currency
,a.DATE
)
END AS cost
FROM execution e
,vehicles v
,allocation a
WHERE e.vehicle = v.vehicle
AND a.part_id = e.part_id
AND a.aloc_sequence = e.aloc_sequence
GROUP BY e.tran_id
,e.SEQUENCE
,date
,a.aloc_sequence
I dont want to include the cost (that has been calculated using case staement in SELECT)
in group by clause ,please help me .
I am using oracle as rdbms

When you use a group by, you are essentially consolidating many rows into one. You either GROUP BY a column, which guarantees that the value is the same for every row in the group, or you aggregate the values is some way (min, max, avg etc)
If you don't specify a column in the group by, there is ambiguity about which value to return (since they aren't in the group by they could be different and you need to be explicit about which one you want)
You could wrap an aggregate around COST i.e. SUM(cost) if you want the sum, or MAX(cost) if they are always going to be the same from row to row (although why not just group by then?)

Related

Group by after a partition by in MS SQL Server

I am working on some car accident data and am stuck on how to get the data in the form I want.
select
sex_of_driver,
accident_severity,
count(accident_severity) over (partition by sex_of_driver, accident_severity)
from
SQL.dbo.accident as accident
inner join SQL.dbo.vehicle as vehicle on
accident.accident_index = vehicle.accident_index
This is my code, which counts the accidents had per each sex for each severity. I know I can do this with group by but I wanted to use a partition by in order to work out % too.
However I get a very large table (I assume for each row that is each sex/severity. When I do the following:
select
sex_of_driver,
accident_severity,
count(accident_severity) over (partition by sex_of_driver, accident_severity)
from
SQL.dbo.accident as accident
inner join SQL.dbo.vehicle as vehicle on
accident.accident_index = vehicle.accident_index
group by
sex_of_driver,
accident_severity
I get this:
sex_of_driver
accident_severity
(No column name)
1
1
1
1
2
1
-1
2
1
-1
1
1
1
3
1
I won't give you the whole table, but basically, the group by has caused the count to just be 1.
I can't figure out why group by isn't working. Is this an MS SQL-Server thing?
I want to get the same result as below (obv without the CASE etc)
select
accident.accident_severity,
count(accident.accident_severity) as num_accidents,
vehicle.sex_of_driver,
CASE vehicle.sex_of_driver WHEN '1' THEN 'Male' WHEN '2' THEN 'Female' end as sex_col,
CASE accident.accident_severity WHEN '1' THEN 'Fatal' WHEN '2' THEN 'Serious' WHEN '3' THEN 'Slight' end as serious_col
from
SQL.dbo.accident as accident
inner join SQL.dbo.vehicle as vehicle on
accident.accident_index = vehicle.accident_index
where
sex_of_driver != 3
and
sex_of_driver != -1
group by
accident.accident_severity,
vehicle.sex_of_driver
order by
accident.accident_severity
You seem to have a misunderstanding here.
GROUP BY will reduce your rows to a single row per grouping (ie per pair of sex_of_driver, accident_severity values. Any normal aggregates you use with this, such as COUNT(*), will return the aggregate value within that group.
Whereas OVER gives you a windowed aggregated, and means you are calculating it after reducing your rows. Therefore when you write count(accident_severity) over (partition by sex_of_driver, accident_severity) the aggregate only receives a single row in each partition, because the rows have already been reduced.
You say "I know I can do this with group by but I wanted to use a partition by in order to work out % too." but you are misunderstanding how to do that. You don't need PARTITION BY to work out percentage. All you need to calculate a percentage over the whole resultset is COUNT(*) * 1.0 / SUM(COUNT(*)) OVER (), in other words a windowed aggregate over a normal aggregate.
Note also that count(accident_severity) does not give you the number of distinct accident_severity values, it gives you the number of non-null values, which is probably not what you intend. You also have a very strange join predicate, you probably want something like a.vehicle_id = v.vehicle_id
So you want something like this:
select
sex_of_driver,
accident_severity,
count(*) as Count,
count(*) * 1.0 /
sum(count(*)) over (partition by sex_of_driver) as PercentOfSex
count(*) * 1.0 /
sum(count(*)) over () as PercentOfTotal
from
dbo.accident as accident a
inner join dbo.vehicle as v on
a.vehicle_id = v.vehicle_id
group by
sex_of_driver,
accident_severity;

TSQL "where ... group by ..." issue that needs solution like "having ..."

I have 3 sub-tables of different formats joined together with unions if this affects anything into full-table. There I have columns "location", "amount" and "time". Then to keep generality for my later needs I union full-table with location-table that has all possible "location" values and other fields are null into master-table.
I query master-table,
select location, sum(amount)
from master-table
where (time...)
group by location
However some "location" values are dropped because sum(amount) is 0 for those "location"s but I really want to have full list of those "location"s for my further steps.
Alternative would be to use HAVING clause but from what I understand HAVING is impossible here because i filter on "time" while grouping on "location" and I would need to add "time" in grouping which destroys the purpose. Keep in mind that the goal here is to get sum(amount) in each "location"
select location, sum(amount)
from master-table
group by location, time
having (time...)
To view the output:
with the first code I get
loc1, 5
loc3, 10
loc6, 1
but I want to get
loc1, 5
loc2, 0
loc3, 10
loc4, 0
loc5, 0
loc6, 1
Any suggestions on what can be done with this structure of master-table? Alternative solution to which I have no idea how to code would be to add numbers from the first query result to location-table (as a query, not actual table) with the final result query that I've posted above.
What you want will require a complete list of locations, then a left-outer join using that table and your calculated values, and IsNull (for tsql) to ensure you see the 0s you expect. You can do this with some CTEs, which I find valuable for clarity during development, or you can work on "putting it all together" in a more traditional SELECT...FROM... statement. The CTE approach might look like this:
WITH loc AS (
SELECT DISTINCT LocationID
FROM location_table
), summary_data as (
SELECT LocationID, SUM(amount) AS location_sum
FROM master-table
GROUP BY LocationID
)
SELECT loc.LocationID, IsNull(location_sum,0) AS location_sum
FROM loc
LEFT OUTER JOIN summary_data ON loc.LocationID = summary_data.LocationID
See if that gets you a step or two closer to the results you're looking for.
I can think of 2 options:
You could move the WHERE to a CASE WHEN construction:
-- Option 1
select
location,
sum(CASE WHEN time <'16:00' THEN amount ELSE 0 END)
from master_table
group by location
Or you could JOIN with the possible values of location (which is my first ever RIGHT JOIN in a very long time 😉):
-- Option 2
select
x.location,
sum(CASE WHEN m.time <'16:00' THEN m.amount ELSE 0 END)
from master_table m
right join (select distinct location from master_table) x ON x.location = m.location
group by x.location
see: DBFIDDLE
The version using T-SQL without CTEs would be:
SELECT l.location ,
ISNULL(m.location_sum, 0) as location_sum
FROM master-table l
LEFT JOIN (
SELECT location,
SUM(amount) as location_sum
FROM master-table
WHERE (time ... )
GROUP BY location
) m ON l.location = m.location
This assumes that you still have your initial UNION in place that ensures that master-table has all possible locations included.
It is the where clause that excludes some locations. To ensure you retain every location you could introduce "conditional aggregation" instead of using the where clause: e.g.
select location, sum(case when (time...) then amount else 0 end) as location_sum
from master-table
group by location
i.e. instead of excluding some rows from the result, place the conditions inside the sum function that equate to the conditions you would have used in the where clause. If those conditions are true, then it will aggregate the amount, but if the conditions evaluate to false then 0 is summed, but the location is retained in the result.

How do I count the rows with a where clause in SQL Server?

I am pretty much stuck with a problem I am facing with SQL Server. I want to show in a query the amount of times that specific value occurs. This is pretty easy to do, but I want to take it a step further and I think the best way to explain on what I am trying to achieve is to explain it using images.
I have two tables:
Plant and
Chest
As you can see with the chest the column 'hoeveelheid' tells how full the chest is, 'vol' == 1 and 3/4 is == 0,75. In the plant table there is a column 'Hoeveelheidperkist' which tells how much plants there can be in 1 chest.
select DISTINCT kist.Plantnaam, kist.Plantmaat, count(*) AS 'Amount'
from kist
group by kist.plantnaam, kist.Plantmaat
This query counts all the chests, but it does not seperate the count of 'Vol' chests and '3/4' chests. It only does This. What I want to achieve is this. But I have no idea how. Any help would be much appreciated.
If you use group by you don't need distinct
and if you want the seprated count for hoeveelheid you ust add to the group by clause
select DISTINCT kist.Plantnaam, kist.Plantmaat, kist.hoeveelheid, count(*) AS 'Amount'
from kist
group by kist.plantnaam, kist.Plantmaat, hoeveelheid
or if you want all the 3 count ond the samw rowx you could use a condition aggreagtion eg:
select DISTINCT kist.Plantnaam, kist.Plantmaat
, sum(case when kist.hoeveelheid ='Vol' then 1 else 0 end) vol
, sum(case when kist.hoeveelheid ='3/3' then 1 else 0 end) 3_4
, count(*) AS 'Amount'
from kist
group by kist.plantnaam, kist.Plantmaat
When you want to filter the data on the counts you have to use having clause. When ever you are using aggregate functions(sum, count, min, max) and you want to filter them on aggregation basis, use having clause
select DISTINCT kist.Plantnaam, kist.Plantmaat, count(*) AS 'Amount'
from kist
group by kist.plantnaam, kist.Plantmaat having count(*) = 1 -- or provide necessary conditions

Combining Count and MIN functions

I have a part of my query as:
SUM(POReceiptQuantity) as Receieved,
MIN(ItemLocalStandardCost) as Low,
MAX(ItemLocalStandardCost) as High,
Received returns the total number of Items we sold this year. The LOW is the lowest price we paid, and High is the highest price we paid.
I'm trying to incorporate a new column showing how many if the item we sold at the Low price. I tried to use Count along with Min function but it returns a "cannot perform an aggregate function on an expression containing an aggregate or a subquery"
Does anyone have any ideas how i could go about this.
Thank you
You need create a subquery with your current GROUP BY query and join with your Original Table. Then you can use a conditional COUNT
SELECT T2.Received,
T2.Low,
COUNT( CASE WHEN T1.ItemLocalStandardCost = T2.Low THEN 1 END) as Total_Low,
T2.High,
COUNT( CASE WHEN T1.ItemLocalStandardCost = T2.High THEN 1 END) as Total_High
FROM YourTable T1
CROSS JOIN ( SELECT SUM(Y.POReceiptQuantity) as Receieved,
MIN(Y.ItemLocalStandardCost) as Low,
MAX(Y.ItemLocalStandardCost) as High
FROM YourTable Y
GROUP BY .... ) as T2

Cumulative substract throught multiple rows

I would like to substract one row from multiple rows. I need to get remaining Quantity (differentiated by BusTransaction_ID and Artikl, and ordered by X_PDateMonth$DATE), which is result of this substract:
And expected results:
Result can be with or without "zero rows". I don't know, how to accomplish this result. And will be better use some "stored procedure" or something, because it will be use to a pretty large data set?
Thanks for all replies.
Here is a solution that works by doing the following:
Calculates the cumulative sums of the values in the first table.
Based on the cumulative sum, determines the value to subtract.
The query looks like this:
select t.bustransaction_id, t.artikl, t.xpldate,
(case when cumeq <= subt.quantity then 0
when cumeq - t.quantity <= subt.quantity
then cumeq - subt.quantity
else t.quantity
end) as newquantity
from (select t.*,
sum(quantity) over (partition by bustransaction_id, artikl order by xpldate) as cumeq
from start_table t
) t left join
subtract_table subt
on t.bustransaction_id = subt.bustransaction_id and
t.artikl = subt.artikl
order by t.bustransaction_id, t.artikl, t.xpldate;
Here is the SQL Fiddle (based on Brians).
This will give you the result with the 'zero rows' using analytic functions:
select x.*,
case
when subqty >= runner
then 0
when runner > subqty
and lag(runner, 1) over( partition by bustransaction_id, artikl
order by bustransaction_id, artikl, xpldate ) > subqty
then quantity
else runner - subqty
end as chk
from (select s.bustransaction_id,
s.artikl,
s.xpldate,
s.quantity,
sum(s.quantity) over( partition by s.bustransaction_id, s.artikl
order by s.bustransaction_id, s.artikl, s.xpldate ) as runner,
z.quantity as subqty
from start_table s
join subtract_table z
on s.bustransaction_id = z.bustransaction_id
and s.artikl = z.artikl) x
order by bustransaction_id, artikl, xpldate
Fiddle: http://sqlfiddle.com/#!6/20987/1/0
The CASE statement combined with the LAG function is what identifies the first "half-depleted" row, which is the biggest piece of your calculation.
In that fiddle I included my derived columns that were necessary to get what you wanted. If you don't want to show those columns you can just select those you need from the inline view, as shown here: http://sqlfiddle.com/#!6/20987/2/0