SQL Count for Each Category - sql

I need to count rows based on 2 fields for grouping.
Animals (a)
id group_id strain_id death_date death_cause status
-----------------------------------------------------------------------
1 512 164 2015-12-01 Culled P
2 512 164 2015-12-02 Culled A
3 512 164 2015-12-02 Surplus B
4 512 230 2015-12-06 Culled A
5 512 164 2015-12-28 Culled A
6 512 230 2016-01-20 Culled B
7 512 230 2016-01-20 Surplus P
8 512 164 NULL NULL P
9 512 230 NULL NULL B
10 512 230 NULL NULL A
11 512 164 2016-01-25 Culled B
12 512 164 2016-02-29 Culled A
13 512 230 2016-02-03 Surplus P
14 512 230 2016-02-03 Culled A
Groups (g)
id group_name
--------------
512 Mice
Strain (s)
id strain_name
----------------
164 Strain 1
230 Strain 2
Group Animal Count (gac)
id total_animals alive_count dead_count
----------------------------------------------------------------------
512 14 3 11
Mating History (mh)
id animal_id history_type history_date
--------------------------------------------------------
1001 2 MA 2015-11-20
1002 2 MR 2015-12-01
1003 3 MA 2015-12-01
1004 6 FA 2015-12-21
1005 9 FA 2016-02-07
1006 10 MA 2016-01-27
1007 11 FA 2015-12-12
So when I group them by the strain_id and the death_cause this is what they should look like visually:
Strain 1 ---- Culled
1 512 164 2015-12-01 Culled P
2 512 164 2015-12-02 Culled A
5 512 164 2015-12-28 Culled A
11 512 164 2016-01-25 Culled B
12 512 164 2016-02-29 Culled A
Strain 1 ---- Surplus
3 512 164 2015-12-02 Surplus B
Strain 2 ---- Culled
4 512 230 2015-12-06 Culled A
6 512 230 2016-01-20 Culled B
14 512 230 2016-02-03 Culled A
Strain 2 ---- Surplus
7 512 230 2016-01-20 Surplus P
13 512 230 2016-02-03 Surplus P
What I want to get from the SQL query is the following result:
g_name s_name d_cause a_total c_alive c_dead c_pup c_breeder c_total
------------------------------------------------------------------------------
Mice Strain 1 Culled 12 3 9 1 2 5
Mice Strain 1 Surplus 12 3 9 0 1 1
Mice Strain 2 Culled 12 3 9 0 1 3
Mice Strain 2 Surplus 12 3 9 2 0 2
Basically I want to count the number of animals using 2 categories which in this case is the strain_name and the death_cause
Note that for an animal to be counted as a breeder (c_breeder), I have to look at the Mating History table and check if the animal_id has ever had any of these codes MA or FA.
I am using INNER JOIN on the groups, group_animal_count, and strains. I use LEFT JOIN for mating_history since animals with a status of P won't have records in that table since they're just pups and won't be involved with mating.

Try:
SELECT group_name, strain_name,death_cause, count(*) as Total
FROM ANIMALS a
JOIN GROUPS g ON a.group_id = g.id
JOIN STRAIN s ON a.strain_id = s.id
GROUP BY group_name, strain_name,death_cause

You can do the aggregation before joining the tables:
SELECT group_name, strain_name, death_cause, total
FROM (
SELECT group_id,
strain_id,
death_cause,
COUNT(*) AS total
FROM animals
GROUP BY group_id, strain_id, death_cause
) a
INNER JOIN groups g ON ( g.group_id = a.group_id )
INNER JOIN strain s ON ( s.strain_id = a.strain_id );

Related

Getting the last 50 rows for each group in group by

I have this query but it is only showing the last 5 rows instead of limiting the amount of rows the group by gets
I only want the last 50 rows for each person to be sum and in the group.
SELECT playerid, SUM(gamesplayed) AS totalgames, SUM(playtimes) AS playtimeTotal, SUM(Kills) AS totalkills
FROM plugin_game
WHERE gamesplayed=1
GROUP BY playerid
ORDER BY totalkills DESC
LIMIT 50
playerid totalgames playtimeTotal totalkills
797749 8 3076 678
53854 8 5982 635
24398 8 3277 575
464657 4 1325 387
65748 4 3390 368
651532 4 3219 354
287378 6 3893 350
753808 4 2565 323
731631 4 1733 256
665338 4 1971 255
569648 2 2041 244
56488 4 2636 157
006985 3 785 93
58640 1 432 72
If i change the LIMIT to 5 it only shows
playerid totalgames playtimeTotal totalkills
797749 8 3076 678
53854 8 5982 635
24398 8 3277 575
464657 4 1325 387
65748 4 3390 368
so if we use 5 games as an example, i only want to get the SUM for the past 5 games for the group
This should work in postgre sql!
SELECT playerid,
SUM(gamesplayed) over w AS totalgames,
SUM(playtimes) over w AS playtimetotal,
SUM(kills) over w AS totalkills,
ROW_NUMBER() over w AS row
FROM plugin_game
window w AS (PARTITION BY playerid ORDER BY totalkills DESC)
WHERE gamesplayed=1 and row <=50

how to update duplicate rows in a column to a new values

I will explain my problem briefly
have duplicate rino like below (actually this rino is the serial number in front end)
chqid rino branchid
----- ---- --------
876 6 2
14 6 2
18 10 2
828 10 2
829 11 2
19 11 2
830 12 2
20 12 2
78 40 2
1092 40 2
1094 41 2
79 41 2
413 43 2
1103 43 2
82 44 2
1104 44 2
1105 45 2
83 45 2
91 46 2
1106 46 2
here in my case I don't want to delete these duplicate rino instead of that I planned to update the rino having max date(column not specified in the above sample actually a date column is there) to the next rino number
what exactly I meant is :
if I sort out the above result according to the max(date) I will get
chqid rino branchid
----- ---- --------
876 6 2
828 10 2
19 11 2
830 12 2
1092 40 2
79 41 2
413 43 2
82 44 2
83 45 2
1106 46 2
(NOTE : total number of duplicate rows are 10 in branchid=2)
the last entered rino in the table for branchid=2 is 245
So I just want to update the 10 rows(Of column rino) with numbers starting from 246 to 255( just added 245+10 like this select lastno+ generate_series(1,10) nos from tab where cola=4 and branchid = 2 and vrid=20;)
Expected Output:
chqid rino branchid
----- ---- --------
876 246 2
828 247 2
19 248 2
830 249 2
1092 250 2
79 251 2
413 252 2
82 253 2
83 254 2
1106 255 2
using postgresql
Finally I found a solution, am using dynamic-sql to solve my issue
do
$$
declare
arow record;
begin
for arow in
select chqid,rino,branchid from (
select chqid,rino::int ,vrid,branchid ,row_number()over (partition by rino::int ) rn
from tab
where vrid =20
and branchid = 2)t
where rn >1
loop
execute format('
update tab
set rino=(select max(rino::int)+1 from gtab19 where acyrid=4 and branchid = 2 and vrid=20)
where chqid=%s
',arow.chqid);
end loop;
end;
$$;

Update or Select into ORACLE

I am using the following statement;
SELECT RESV_ID, BOOKING_CUS_ID, ACC_ID,
(SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.IN_FLIGHT_ID) AS DEPART_FLIGHT_PRICE,
(SELECT F1.FLI_PRICE FROM FLIGHT F1 WHERE F1.FLI_ID = R.OUT_FLIGHT_ID) AS RETURN_FLIGHT_PRICE,
(SELECT AC.ACC_PRICEPN FROM ACCOMMODATION AC WHERE AC.ACC_ID = R.ACC_ID) AS ACCOMMODATION_PRICE
FROM HOLIDAY_RESERVATION R;
to yield the following results;
RESV_ID BOOKING_CUS_ID ACC_ID DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
---------- -------------- ---------- ------------------- ------------------- -------------------
1 1 2 520 450 350
2 3 4 250 150 150
3 5 6 290 300 450
4 7 7 399 450 650
5 9 365 345
6 11 558 460
7 13 250 250
8 15 550 550
9 17 25 250
10 19 19 450
10 rows selected.
Question:
How do I sum up the price fields, SOME PRICES ARE NOT AVAILABLE because a reservation was either made for accommodation only or flight only, hence both values will not be present always and this is where the issue lies
DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
Furthermore:
I wish to insert or update the SUM of those three values into a SUBTOTAL in the reservation table, perhaps by using select into or update, I have spent a whole day trying to do this but my skills are limited. any help will be greatly appreciated.
Flight table
FLI_ID FLI_CO FLI_AIRCRA DEPT_AIRPORT ARRV_AIRPORT DEPT_TIME ARRV_TIME FLI_PRICE
1 BD425 Boeing 707 1 12 18-MAR-12 02.24.00 AM 18-MAR-12 06.24.00 AM 520
2 LX345 Beriev 30 6 7 20-MAR-12 03.30.00 PM 20-MAR-12 04.20.00 PM 250
3 NZ4445 Boeing 720 9 14 25-MAR-12 09.00.00 AM 25-MAR-12 05.00.00 PM 290
4 TP351 Boeing 767 10 15 25-MAR-12 11.25.00 AM 25-MAR-12 03.35.00 PM 399
5 BA472 Boeing 720 5 14 26-MAR-12 01.05.00 PM 26-MAR-12 04.15.00 PM 365
Accommodation
ACC_ID ACC_TYPE_CODE ACC_DESC ACC_PRICEPN ACC_ROOMS RESORT_ID ACC_ADDR CITY_ID
1 1 Three bedroom bungalow near theme park 500 3 1
2 1 Two bedroom bungalow next to disney house 350 2 1
3 1 One bedroom bungalow with lake view 250 2 2
4 2 One bedroom chalet near the lake 150 1 2
5 2 Four bedroom chalet near the tree house 600 4 3
Reservation
RESV_ID EMP_ID BOOKING_CUS_ID RESV_DATE HOLIDAY_S HOLIDAY_E IN_FLIGHT_ID OUT_FLIGHT_ID IN_FLIGHT_SEATS_NO OUT_FLIGHT_SEATS_NO ACC_ID SUBTOTAL
1 338 1 16-FEB-12 18-MAR-12 20-APR-12 1 11 2 2 2
2 335 3 10-JAN-12 20-MAR-12 22-APR-12 2 12 2 2 4
3 338 5 05-MAR-12 25-MAR-12 26-APR-12 3 13 2 2 6
4 328 7 02-JAN-12 25-MAR-12 25-APR-12 4 14 2 2 7
5 311 9 20-JAN-12 26-MAR-12 21-APR-12 5 15 2 2
6 317 11 07-JAN-12 27-MAR-12 22-APR-12 6 16 2 2
7 344 13 29-FEB-12 15-MAR-12 12-APR-12 7 17 2 2
8 326 15 11-JAN-12 18-MAR-12 12-APR-14 8 18 2 2
9 329 17 16-JAN-12 19-MAR-12 17-APR-12 25
10 323 19 18-FEB-12 20-MAR-12 21-APR-12 19
Okay I managed to yield the results that i wanted
SELECT HR.RESV_ID, F_IN.FLI_ID, F_IN.FLI_PRICE, F_OUT.FLI_ID, F_OUT.FLI_PRICE, AC.ACC_ID, AC.ACC_PRICEPN, NVL(F_IN.FLI_PRICE,0)+NVL(F_OUT.FLI_PRICE,0)+NVL(AC.ACC_PRICEPN,0) AS TOTAL
FROM HOLIDAY_RESERVATION HR
LEFT JOIN FLIGHT F_IN ON HR.IN_FLIGHT_ID = F_IN.FLI_ID
LEFT JOIN FLIGHT F_OUT ON HR.OUT_FLIGHT_ID = F_OUT.FLI_ID
LEFT JOIN ACCOMMODATION AC ON HR.ACC_ID = AC.ACC_ID
ORDER BY HR.RESV_ID;
YIELDS
RESV_ID FLI_ID FLI_PRICE FLI_ID FLI_PRICE ACC_ID ACC_PRICEPN TOTAL
---------- ---------- ---------- ---------- ---------- ---------- ----------- ----------
1 1 500 11 555 2 350 1405
2 2 150 12 253 4 150 553
3 3 300 13 345 6 450 1095
4 4 450 14 343 7 650 1443
5 5 345 15 242 587
6 6 460 16 460 920
7 7 250 17 250 500
8 8 550 18 550 1100
9 25 250 250
10 19 450 450
And the following statement is to update the reservation table.
Thanks to Leigh Riffel from DBA stackxchange for the following code
UPDATE HOLIDAY_RESERVATION R SET SUBTOTAL =
NVL((SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.IN_FLIGHT_ID), 0) +
NVL((SELECT F.FLI_PRICE FROM FLIGHT F WHERE F.FLI_ID = R.OUT_FLIGHT_ID), 0) +
NVL((SELECT AC.ACC_PRICEPN FROM ACCOMMODATION AC WHERE AC.ACC_ID = R.ACC_ID), 0);
Now the subtotal is populated with the values obtained from the sum performed above >>
RESV_ID EMP_ID BOOKING_CUS_ID RESV_DATE HOLIDAY_S HOLIDAY_E IN_FLIGHT_ID OUT_FLIGHT_ID IN_FLIGHT_SEATS_NO OUT_FLIGHT_SEATS_NO ACC_ID SUBTOTAL
---------- ---------- -------------- --------- --------- --------- ------------ ------------- ------------------ ------------------- ---------- ----------
1 338 1 16-FEB-12 18-MAR-12 20-APR-12 1 11 2 2 2 1405
2 335 3 10-JAN-12 20-MAR-12 22-APR-12 2 12 2 2 4 553
3 338 5 05-MAR-12 25-MAR-12 26-APR-12 3 13 2 2 6 1095
4 328 7 02-JAN-12 25-MAR-12 25-APR-12 4 14 2 2 7 1443
5 311 9 20-JAN-12 26-MAR-12 21-APR-12 5 15 2 2 587
6 317 11 07-JAN-12 27-MAR-12 22-APR-12 6 16 2 2 920
7 344 13 29-FEB-12 15-MAR-12 12-APR-12 7 17 2 2 500
8 326 15 11-JAN-12 18-MAR-12 12-APR-14 8 18 2 2 1100
9 329 17 16-JAN-12 19-MAR-12 17-APR-12 25 250
10 323 19 18-FEB-12 20-MAR-12 21-APR-12 19 450
Subsequently the code was added to a trigger (which was the original intention)
CREATE OR REPLACE TRIGGER HR_SUBTOTAL
BEFORE INSERT OR UPDATE ON HOLIDAY_RESERVATION
FOR EACH ROW
BEGIN
SELECT
NVL((SELECT F.Fli_Price FROM Flight F WHERE F.Fli_ID = :new.In_Flight_ID), 0) +
NVL((SELECT F.Fli_Price FROM Flight F WHERE F.Fli_ID = :new.Out_Flight_ID), 0) +
NVL((SELECT AC.Acc_PricePn FROM Accomodation AC WHERE AC.Acc_ID = :new.Acc_ID), 0)
INTO :new.Subtotal
FROM dual;
END;
/
For the SUM, assuming you want to treat NULL values as 0, you'd just need to do an NVL on the numbers
NVL( DEPART_FLIGHT_PRICE, 0 ) +
NVL( RETURN_FLIGHT_PRICE, 0 ) +
NVL( ACCOMMODATION_PRICE, 0 )
As for the UPDATE, it sounds like you just need a correlated UPDATE statement.
UPDATE reservation r
SET subtotal = (SELECT (SELECT NVL( DEPART_FLIGHT_PRICE, 0 ) +
NVL( RETURN_FLIGHT_PRICE, 0 ) +
NVL( ACCOMMODATION_PRICE, 0 )
FROM (SELECT RESV_ID,
BOOKING_CUS_ID,
ACC_ID,
(SELECT F.FLI_PRICE
FROM FLIGHT F
WHERE F.FLI_ID = R.IN_FLIGHT_ID) AS DEPART_FLIGHT_PRICE,
(SELECT F1.FLI_PRICE
FROM FLIGHT F1
WHERE F1.FLI_ID = R.OUT_FLIGHT_ID) AS RETURN_FLIGHT_PRICE,
(SELECT AC.ACC_PRICEPN
FROM ACCOMMODATION AC
WHERE AC.ACC_ID = R.ACC_ID) AS ACCOMMODATION_PRICE
FROM dual));
You are asking:
How do I sum up the price fields, as you can see some of them can have nulls.
DEPART_FLIGHT_PRICE RETURN_FLIGHT_PRICE ACCOMMODATION_PRICE
Just enclose them in NVL function as follows:
NVL(DEPART_FLIGHT_PRICE, 0)
and then sum them up.
For the second part, what you need is a MERGE statement. A good example is at http://www.oracle-developer.net/display.php?id=203

Stock on the fly missing values with no sales

I have the following queries,
QryStockOnHand
SELECT QrySaleTot.Item, QrySaleTot.ProductID, [QryStockLevel].[Stock]-[QrySaleTot].[Quantity] AS StockOnHand
FROM QryStockLevel INNER JOIN QrySaleTot ON QryStockLevel.ProductID = QrySaleTot.ProductID;
QrySaleTot
SELECT TblProduct.Item, Sum(TblTotalSale.Size) AS Quantity, TblProduct.ProductID
FROM TblProduct INNER JOIN TblTotalSale ON TblProduct.[ProductID] = TblTotalSale.[ProductID]
GROUP BY TblProduct.Item, TblProduct.ProductID;
QryStockLevel
SELECT TblStock.ProductID, Sum(TblStock.StockLevel) AS Stock, TblProduct.Item
FROM TblStock INNER JOIN TblProduct ON TblStock.ProductID = TblProduct.ProductID
GROUP BY TblStock.ProductID, TblProduct.Item;
When I run the QryStockonHand and no sales of a product have been made then the porduct does not appear in the result of the query...
Sample Data
TblStock
StockID ProductID StockLevel
138 1 528
139 3 528
140 5 528
141 9 528
142 7 528
143 18 80
144 30 72
145 34 72
146 33 72
147 32 200
148 22 80
149 19 80
150 23 80
151 20 80
TblProduct
ProductID Item Price StockDelivery PriceSmall Large Small
1 Carling £2.50 528 £1.40 2 1
3 Carlsburg £2.70 528 £1.60 2 1
5 IPA £2.30 528 £1.20 2 1
7 StrongBow £2.80 528 £1.65 2 1
9 RevJames £2.45 528 £1.30 2 1
11 Becks £2.90 72 1
12 WKDBlue £2.80 72 1
13 WKDRed £2.80 72 1
14 SmirnoffIce £2.80 72 1
TblTotalSale
TotalSalesID ProductID SalePrice Day Time Size
576 1 £1.40 19/02/2012 15:34:24 1
528 1 £2.50 09/02/2012 14:44:44 2
530 1 £1.40 09/02/2012 14:44:44 1
565 1 £2.50 19/02/2012 15:34:24 2
567 1 £1.40 19/02/2012 15:34:24 1
570 3 £2.70 19/02/2012 15:34:24 2
571 3 £1.60 19/02/2012 15:34:24 1
577 3 £2.70 19/02/2012 15:34:24 2
578 3 £1.60 19/02/2012 15:34:24 1
533 3 £2.70 09/02/2012 14:44:44 2
534 3 £1.60 09/02/2012 14:44:44 1
Any Idea why... I guess it is a null thing, where it is seeing the no sales as a non existent thing, instead of a zero sales.... any idea how i could fix it?
Thanks
Sam
Instead of an inner join, use a left outer join, which will tell it to grab all of the rows from the left hand table on the join, instead of an inner join, which returns only rows which have values in both tables.
I don't know the QryStockLevel fields, but your query should look something like this:
SELECT QryStockLevel.Item, QryStockLevel.ProductID, [QryStockLevel].[Stock]-NZ([QrySaleTot].[Quantity],0) AS StockOnHand
FROM QryStockLevel LEFT OUTER JOIN QrySaleTot ON QryStockLevel.ProductID = QrySaleTot.ProductID;
Note the NZ function to handle a null on the Quantity when qrysaletot does not have a row.

SQL query self join

I am working on a query for a report in Oracle 10g.
I need to generate a short list of each course along with the number of times they were offered in the past year (including ones that weren't actually offered).
I created one query
SELECT coursenumber, count(datestart) AS Offered
FROM class
WHERE datestart BETWEEN (sysdate-365) AND sysdate
GROUP BY coursenumber;
Which produces
COURSENUMBER OFFERED
---- ----------
ST03 2
PD01 1
AY03 2
TB01 4
This query is all correct. However ideally I want it to list those along with COURSENUMBER HY and CS in the left column as well with 0 or null as the OFFERED value. I have a feeling this involves a join of sorts, but so far what I have tried doesn't produce the classes with nothing offered.
The table normally looks like
REFERENCE_NO DATESTART TIME TIME EID ROOMID COURSENUMBER
------------ --------- ---- ---- ---------- ---------- ----
256 03-MAR-11 0930 1100 2 2 PD01
257 03-MAY-11 0930 1100 12 7 PD01
258 18-MAY-11 1230 0100 12 7 PD01
259 24-OCT-11 1930 2015 6 2 CS01
260 17-JUN-11 1130 1300 6 4 CS01
261 25-MAY-11 1900 2000 13 6 HY01
262 25-MAY-11 1900 2000 13 6 HY01
263 04-APR-11 0930 1100 13 5 ST03
264 13-SEP-11 1930 2100 6 4 ST03
265 05-NOV-11 1930 2100 6 5 ST03
266 04-FEB-11 1430 1600 6 5 ST03
267 02-JAN-11 0630 0700 13 1 TB01
268 01-FEB-11 0630 0700 13 1 TB01
269 01-MAR-11 0630 0700 13 1 TB01
270 01-APR-11 0630 0700 13 1 TB01
271 01-MAY-11 0630 0700 13 1 TB01
272 14-MAR-11 0830 0915 4 3 AY03
273 19-APR-11 0930 1015 4 3 AY03
274 17-JUN-11 0830 0915 14 3 AY03
275 14-AUG-09 0930 1015 14 3 AY03
276 03-MAY-09 0830 0915 14 3 AY03
SELECT
coursenumber,
COUNT(CASE WHEN datestart BETWEEN (sysdate-365) AND sysdate THEN 1 END) AS Offered
FROM class
GROUP BY coursenumber;
So, as you can see, this particular problem doesn't need a join.
I think something like this should work for you, by just doing it as a subquery.
SELECT distinct c.coursenumber,
(SELECT COUNT(*)
FROM class
WHERE class.coursenumber = c.coursenumber
AND datestart BETWEEN (sysdate-365) AND sysdate
) AS Offered
FROM class c
I like jschoen's answer better for this particular case (when you want one and only one row and column out of the subquery for each row of the main query), but just to demonstrate another way to do it:
select t1.coursenumber, nvl(t2.cnt,0)
from class t1 left outer join (
select coursenumber, count(*) cnt
from class
where datestart between (sysdate-365) AND sysdate
group by coursenumber
) t2 on t1.coursenumber = t2.coursenumber