sql find max value of a column and select as new field - sql

I am currently working with some ambulance data and in some scenario's multiple vehicles are dispatched to the same call. Of those dispatched sometimes they are stood down(diverted to a different call.)
The relevent fields are incidentID, VehicleAllocationSequenceNumber snd VehicleArrivalAtSceneDateTime.
An incidentID can map to multiple rows with different VehicleAllocationSequenceNumber and VehicleArrivalAtSceneDateTime.
VehicleAllocationSequenceNumber defines the order in which the vehicles arrived on the scene. VehicleArrivalAtSceneDateTime is either a datetime or NULL if the vehicle never arrived.
I want to select distinct IncidentID's with and condense the other columns into two new features; Number_of_Vehicles_assigned and number_of_vehicles_on_scene
I am fairly new to queries of this complexity and I am looking for a way to do this without using a cursor.
Thanks in advance
Edit:
Nevermind I nailed the query, think I'm just a bit scatter brained this morning. Here is my query for posterity's sake.
Feedback is welcome:
select
IncidentID,
max(VehicleAllocationSequenceNumber) as number_vehicles_assigned,
count(VehicleArrivalAtSceneDateTime) as number_of_vehicles_on_scene
FROM
[dwuserobj].[acc].[AmbulanceLinkedDatasetForModelling]
where
IncidentID in(
select distinct
IncidentID
from
[dwuserobj].[acc].[AmbulanceLinkedDatasetForModelling]
)
and vehicleArrivalAtSceneDateTime is not null
group by
IncidentID;

Use a case with a not exists
select distinct incidentID,
case
when not exists
(
select 1
from AmbulanceTable t2
where t2.incidentID = t1.incidentID
and t2.VehicleAllocationSequenceNumber is null
) then 1
else 0 as alloc,
case
when not exists
(
select 1
from AmbulanceTable t3
where t3.incidentID = t1.incidentID
and t3.VehicleArrivalAtSceneDateTime is null
) then 1
else 0 as onscene
from AmbulanceTable t1
Edit:
So no_vehicles is number of vehicles... my bad:
select incidentid
count(VehicleAllocationSequenceNumber) as num_assigned,
count(VehicleArrivalAtSceneDateTime ) as num_oncscene
from AmbulanceLinkedDatasetForModelling
group by incidentid
this will give you zeros if none assigned/on scene

this should provide you with the logic you need. I made a temp table so you can see how the incidentID interacts with the AllocationSequenceNumber and the SceneDateTime.
DECLARE #AmbulanceLinkedDatasetForModelling TABLE (IncidentID int, VehicleAllocationSequenceNumber int, VehicleArrivalAtSceneDateTime datetime)
INSERT INTO #AmbulanceLinkedDatasetForModelling
SELECT 1, 500, NULL UNION ALL -- the vehicle never arrived
SELECT 2, 501, '2016-05-20 06:07:00.370' UNION ALL
SELECT 3, 502, '2018-01-05 08:15:08.970' UNION ALL
SELECT 4, 503, NULL UNION ALL -- the vehicle never arrived
SELECT 4, 504, '2017-04-11 11:22:01.360'UNION ALL
SELECT 4, 505, '2017-04-11 11:32:01.360'
SELECT
IncidentID,
number_vehicles_assigned=COUNT(DISTINCT VehicleAllocationSequenceNumber),
no_vehicles_on_scene=SUM(CASE WHEN VehicleArrivalAtSceneDateTime IS NOT NULL THEN 1 ELSE 0 END)
FROM
#AmbulanceLinkedDatasetForModelling
GROUP BY
IncidentID

Related

Aggregate on a non group by column check if any value matches a criteria

Let's say I have a table Category with columns
id, childCategory, hasParts
Let's say I want to group by id and check if any value in hasParts has value true.
How to do this efficiently?
this has got to be the most vague post that i've seen on here but i'll take a stab at it. based on my own imagination and the 3 sentences provided, here we go:
create table category (id int, childcategory nvarchar(25), hasparts bit)
insert category
select 1, 'stroller', 1
union all
select 1, 'rocker', 1
union all
select 2, 'car', 0
union all
select 2, 'doll', 0
union all
select 3, 'nasal sprayer', 0
union all
select 3, 'thermometer', 1
select *,
case when exists (select 1 from category b where a.id = b.id and b.hasparts = 1) then 'has true value' end as truecheck
from
(
select id, count(*) as inventory
from category
group by id
) a
drop table category
this should theoretically get you want you want. adjust as needed.

Combine multiple boolean columns into a single column

I am generation reports from an ERP system where users are provided with a check box which return a boolean value for each item selected. The database is hosted on SQL Server.
However, users can select Contracts with other values as well, as shown below.
I would like to capture the Categories as a single column and I don't mind having duplicate rows in the view. I would like the first row to return Contract and the second the other value selected, for the same Reference ID.
You can use apply :
select distinct t.*, tt.category
from t cross apply
( values ('Contracts', t.Contracts),
('Tender', t.Tender),
('Waiver', t.Waiver),
('Quotation', t.Quotation)
) tt(category, flag)
where flag = 1;
I guess a straightforward way is:
select *, 'Contract' as [Category] from [TableOne] where [Contract] = 1
union all select *, 'Tender' as [Category] from [TableOne] where [Tender] = 1
union all select *, 'Waiver' as [Category] from [TableOne] where [Waiver] = 1
union all select *, 'Quotation' as [Category] from [TableOne] where [Quotation] = 1
union all select *, '(none)' as [Category] from [TableOne] where [Contract]+[Tender]+[Waiver]+[Quotation] = 0
order by [Reference ID]
Note that the last line is put there just in case you need to handle the all-zero case.

Excluding a row that contains a specific value

I want to exclude people who have joined a specific group. For example, if some students signed up for an Orchestra club, and I want to retrieve a list of students who did NOT sign up for orchestra, how do I do so?
I am unable to simply do a Group By clause because some students may have joined multiple clubs, and would bypass the Where condition and still show up in the query,
as shown here.
I am thinking about using a CASE statement in the SELECT clause to flag the person as '1' if they have joined Orchestra, and '0' if they have not, but I'm struggling to write an aggregate CASE function, which would cause issues from the GROUP BY clause.
Any thoughts on how to flag people with a certain row value?
Apparently my table didn't get saved onto SQLFiddle so you can paste the code below on your own screen:
CREATE TABLE activity ( PersonId, Club) as
select 1, 'Soccer' from dual union
select 1, 'Orchestra' from dual union
select 2, 'Soccer' from dual union
select 2, 'Chess' from dual union
select 2, 'Bball' from dual union
select 3, 'Orchestra' from dual union
select 3, 'Chess' from dual union
select 3, 'Bball' from dual union
select 4, 'Soccer' from dual union
select 4, 'Bball' from dual union
select 4, 'Chess' from dual;
Use the HAVING clause instead of using WHERE, with case expression :
HAVING max(case when column = ‘string’ then 1 else 0 end) = 0
Add this after your group by .
How about selecting a list of user ids from the activity table and excluding it:
SELECT * FROM users WHERE id NOT IN
(SELECT PersonId FROM activity WHERE Club = 'Orchestra');
You could use a subquery to return a list of people to exclude.
-- Returns person 2 and 4.
SELECT
PersonId
FROM
activity
WHERE
PersonId NOT IN
(
-- People to exclude.
SELECT
PersonId
FROM
activity
WHERE
Club = 'Orchestra'
)
GROUP BY
PersonId
;
EDIT Removed superfluous distinct in subquery - thanks #mathguy.
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =1; --> get some students signed up for an Orchestra club
select * from
(
select a.*, case when Club ='Orchestra' then 1 else 0 end flag
from activity a
) where flag =0; --> get students not signed up for an Orchestra club

use SUM on certain conditions

I have a script that extracts transactions and their details from a database. But my users complain that the file size being generated is too large, and so they asked for certain transactions to be just summed up/consolidated instead if they are of a certain classification, say Checking Accounts. That means there should only be one line in the result set named "Checking" which contains the sum of all transactions under Checking Accounts. Is there a way for an SQL script to go like:
CASE
WHEN Acct_class = 'Checking'
then sum(tran_amount)
ELSE tran_amount
END
I already have the proper GROUP BY and ORDER BY statements, but I can't seem to get my desired output. There is still more than one "Checking" line in the result set. Any ideas would be very much appreciated.
Try This,
Select sum(tran_amount) From tran_amount Where Acct_class = 'Checking'
You can try to achieve this using UNION ALL
SELECT tran_amount, .... FROM table WHERE NOT Acct_class = 'Checking'
UNION ALL
SELECT SUM(tran_amount), .... FROM table WHERE Acct_class = 'Checking' GROUP BY Acct_class, ...;
hi you can try below sql
select account_class,
case when account_class = 'saving' then listagg(trans_detail, ',') within group (order by emp_name) -- will give you all details transactions
when account_class = 'checking' then to_char(sum(trans_detail)) -- will give you only sum of transactions
end as trans_det from emp group by account_class;
Or, if your desired output is getting either the sum, either the actual column value based on another column value, the solution would be to use an analytical function to get the sum together with the actual value:
select
decode(acct_class, 'Checking', tran_amount_sum, tran_amount)
from (
select
sum(tran_amount) over (partition by acct_class) as tran_amount_sum,
tran_amount,
acct_class
from
YOUR_TABLE
)
You can try something like the following, by keeping single rows for some classes, and aggregating for some others:
with test (id, class, amount) as
(
select 1, 'a' , 100 from dual union all
select 2, 'a' , 100 from dual union all
select 3, 'Checking', 100 from dual union all
select 4, 'Checking', 100 from dual union all
select 5, 'c' , 100 from dual union all
select 6, 'c' , 100 from dual union all
select 7, 'c' , 100 from dual union all
select 8, 'd' , 100 from dual
)
select sum(amount), class
from test
group by case
when class = 'Checking' then null /* aggregates elements of class 'b' */
else id /* keeps elements of other classes not aggregated */
end,
class

Using recursive sql query not for parent-child

I'm not new in sql and t-sql, but at past I've never used recursive query - all problems were solved with WHILE or CURSOR. I just got 1 question - how to organaze recursion query for following problem: I want to manipulate with last row of data in certain partition. Can't understand how to stop my recursion at last level of partition.
CREATE TABLE #temp
(i int
, s int
, v int);
INSERT INTO #temp
SELECT 1, 1, 10
UNION
SELECT 1, 2, 20
UNION
SELECT 2, 1, 5
UNION
SELECT 2, 2, 5
UNION
SELECT 2, 3, 2
WITH CTE AS
(
SELECT i
, s
, v
FROM #temp
WHERE s=1
UNION ALL
SELECT t.i
, t.s
, t.v + cte.v as new_v
FROM #temp t
INNER JOIN cte
ON (cte.i=t.i)
WHERE t.s>1
)
SELECT *
FROM cte
OPTION(MAXRECURSION 0)
I want to get 5 rows as result:
result
I know that it could be solved with OUTER APPLY, JOINS, WHILE or CURSOR methods. Could you please share any features for my to understand how to get same result with recurcive cte query? SUM function there is just for example - for that problem recurcive query is best way cause I will use many scalar functions in big CASE which will use value from last row in partition and value of current row partition.
Thanks.
Sorry for my bad english level.
Will it be correctly if I'll try same problem with following example? I guess that need to correctly say in which order way recursive query gonna do any data manipulating. So below code which will help you understand what did I want to solve:
CREATE TABLE #temp
(i_key int
, step int
, step_h int
, value int);
INSERT INTO #temp
SELECT 1, 1, NULL, 20
UNION
SELECT 1, 2, 1, 20
UNION
SELECT 2, 1, NULL, 10
UNION
SELECT 2, 2, 1, 10
UNION
SELECT 2, 3, 2, 5
WITH CTE AS
(
SELECT i_key
, step
, value
FROM #temp
WHERE step=1
--AND i_key=2
UNION ALL
SELECT t.i_key
, t.step
, CASE
WHEN cte.value - t.value <=0 THEN 0
ELSE cte.value - t.value
END as value
FROM #temp t
INNER JOIN cte
ON (cte.i_key=t.i_key
AND cte.step=t.step_h)
--WHERE t.step>1
)
SELECT *
FROM CTE
OPTION(MAXRECURSION 0)
Is parent-child structure always need for solving this problems?
So i guess it could be done with another join (without column of parent-child).
AND cte.step=t.step-1
For your particular example, recursion is unnecessary. All you need is SQL Server 2012 or later version:
select t.*,
sum(t.v) over(partition by t.i order by t.s) as [RT]
from #temp t
order by t.i, t.s;
If you need to access previos / next row, there are lag() / lead() ranking functions that were introduced in the same aforementioned version of SQL Server.
EDIT: Ah, I see. You simply want to know how to write recursive CTEs properly. Here is a (seemingly) correct code for your second example:
with cte as (
select t.i_key, t.step, t.value
from #temp t
where t.step_h is null
union all
select c.i_key, t.step, case
when c.value < t.value then 0
else c.value - t.value
end as [Value]
from #temp t
inner join cte c on c.step = t.step_h
and c.i_key = t.i_key
)
select *
from cte c
order by c.i_key, c.step;
In the end, it stops by itself when an iteration does not produce any new rows.