just a quick and easy question, nothing too complicated.
I'm making a function in MySQL that returns total revenue from a specific flight and my question is this; can I COUNT() WHERE the values of paymentType = 1, and then multiply them by the price of a flight ticket? I did try this, but it didn't multiply for some reason.
Example of what I'm thinking:
SELECT COUNT(paymentType * 400) FROM booking
WHERE paymentType = 1;
Here is the code between begin and end$$.
DECLARE revenue INT;
SELECT COUNT(paymentType) INTO revenue FROM booking
INNER JOIN flights
ON booking.flightCode = flights.flightCode
WHERE paymentType = 1
AND flights.flightDate = flight_date
AND flights.flightNumber = flight_number;
RETURN revenue;
count() just checks if the specified field is non-null, and counts that as 1, regardless of the value in there. count(somefield*400) and count(somefield) will basically all give the exact same count
If you want to multiple the count ITSELF by 400, then you'd need
select count(somefield) * 400 as result
Related
I currently have an employee logging sql table that has 3 columns
fromState: String,
toState: String,
timestamp: DateTime
fromState is either In or Out. In means employee came in and Out means employee went out. Each row can only transition from In to Out or Out to In.
I'd like to generate a temporary table in sql to keep track during a given hour (hour by hour), how many employees are there in the company. Aka, resulting table has columns HourBucket, NumEmployees.
In non-SQL code I can do this by initializing the numEmployees as 0 and go through the table row by row (sorted by timestamp) and add (employee came in) or subtract (went out) to numEmployees (bucketed by timestamp hour).
I'm clueless as how to do this in SQL. Any clues?
Use a COUNT ... GROUP BY query. Can't see what you're using toState from your description though! Also, assuming you have an employeeID field.
E.g.
SELECT fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable
INNER JOIN (SELECT employeeID AS 'empID', MAX(timestamp) AS 'latest' FROM StaffinBuildingTable GROUP BY employeeID) AS LastEntry ON StaffinBuildingTable.employeeID = LastEntry.empID
GROUP BY fromState
The LastEntry subquery will produce a list of employeeIDs limited to the last timestamp for each employee.
The INNER JOIN will limit the main table to just the employeeIDs that match both sides.
The outer GROUP BY produces the count.
SELECT HOUR(SBT.timestamp) AS 'Hour', SBT.fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable AS SBT
INNER JOIN (
SELECT SBIJ.employeeID AS 'empID', MAX(timestamp) AS 'latest'
FROM StaffinBuildingTable AS SBIJ
WHERE DATE(SBIJ.timestamp) = CURDATE()
GROUP BY SBIJ.employeeID) AS LastEntry ON SBT.employeeID = LastEntry.empID
GROUP BY SBT.fromState, HOUR(SBT.timestamp)
Replace CURDATE() with whatever date you are interested in.
Note this is non-optimal as it calculates the HOUR twice - once for the data and once for the group.
Again you are using the INNER JOIN to limit the number of returned row, this time to the last timestamp on a given day.
To me your description of the FromState and ToState seem the wrong way round, I'd expect to doing this based on the ToState. But assuming I'm wrong on that the following should point you in the right direction:
First, I create a "Numbers" table containing 24 rows one for each hour of the day:
create table tblHours
(Number int);
insert into tblHours values
(0),(1),(2),(3),(4),(5),(6),(7),
(8),(9),(10),(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),(21),(22),(23);
Then for each date in your employee logging table, I create a row in another new table to contain your counts:
create table tblDailyHours
(
HourBucket datetime,
NumEmployees int
);
insert into tblDailyHours (HourBucket, NumEmployees)
select distinct
date_add(date(t.timeStamp), interval h.Number HOUR) as HourBucket,
0 as NumEmployees
from
tblEmployeeLogging t
CROSS JOIN tblHours h;
Then I update this table to contain all the relevant counts:
update tblDailyHours h
join
(select
h2.HourBucket,
sum(case when el.fromState = 'In' then 1 else -1 end) as cnt
from
tblDailyHours h2
join tblEmployeeLogging el on
h2.HourBucket >= el.timeStamp
group by h2.HourBucket
) cnt ON
h.HourBucket = cnt.HourBucket
set NumEmployees = cnt.cnt;
You can now retrieve the counts with
select *
from tblDailyHours
order by HourBucket;
The counts give the number on site at each of the times displayed, if you want during the hour in question, we'd need to tweak this a little.
There is a working version of this code (using not very realistic data in the logging table) here: rextester.com/DYOR23344
Original Answer (Based on a single over all count)
If you're happy to search over all rows, and want the current "head count" you can use this:
select
sum(case when t.FromState = 'In' then 1 else -1) as Heads
from
MyTable t
But if you know that there will always be no-one there at midnight, you can add a where clause to prevent it looking at more rows than it needs to:
where
date(t.timestamp) = curdate()
Again, on the assumption that the head count reaches zero at midnight, you can generalise that method to get a headcount at any time as follows:
where
date(t.timestamp) = "CENSUS DATE" AND
t.timestamp <= "CENSUS DATETIME"
Obviously you'd need to replace my quoted strings with code which returned the date and datetime of interest. If the headcount doesn't return to zero at midnight, you can achieve the same by removing the first line of the where clause.
table name is data.
Columns - 'date', 'location, 'fp, 'TV'
Under date I will have multiple different dates but each date has a number of rows with the same date. Same with location.
I am trying to work out the average of TV for every time the date and location are the same and fp = 1, and insert the result into a new column called avgdiff
So I might have a number of rows with the date 2016-12-08 and location LA, with different numbers under fp and TV. So when the date is 2016-12-08 and location is LA, fp might equal 1, 4 times, and TV for those 4 rows might be 7.4, 8.2, 1, -2. So the avg will be 3.65.
I think I need to use avg and count functions with conditions but I am having a lot of trouble with this. I hope this makes sense.
Thanks
You can query for the average using a GROUP BY:
SELECT `date`, `location`, AVG(`TV`) AS `avgtv`
FROM `data`
WHERE `fp` = 1
GROUP BY `date`, `location`
To update another table with your computed averages (which I strongly recommend against), you can use an UPDATE...JOIN with the above as a subquery:
UPDATE ratings r
JOIN ( /* paste above query here */ ) t
ON t.date = r.date AND t.location = r.location
SET r.avgtv = t.avgtv
If, for any reason, you cannot avoid storing aggregated data in the same table (thereby introducing redundancy and possibly incorrect/not up to date values), do an update statement of the following form:
update data,
(select t2.location, t2.date, avg(t2.TV) as avgTV2
from data t2
where t2.fp = 1
group by t2.location, t2.date) aggValues
set avgTV = avgTV2
where data.location = aggValues.location
and data.date = aggValues.date
and data.fp = 1
I have the following query to update my payment table in order to set it equal to the sum of all charges minus the sum of all payments and credits in a customer database.
This works perfectly as long as there is a charge, however, if the first subquery is equal to zero it does not update payment.balance to a negative number, it simply remains zero.
Can anyone tell me how to fix this or why this is?
UPDATE customer
SET balance = (SELECT SUM(amount)
FROM payment
WHERE type = 'C'
AND custID = '10003')
- (SELECT SUM(amount)
FROM payment
WHERE (type = 'P' OR type = 'X')
AND custID = '10003')
WHERE custID = '10003';
So in brief summary, when the first subquery (SELECT SUM(amount) FROM payment WHERE type = 'C' AND custID = '10003') is 0, the update always results in 0 instead of 0 minus the second subquery.
Thoughts?
Being that I cannot see your actual db table column setup I'm guessing this.
MySQL SUM() function returns the sum of an expression. SUM() function returns NULL when the return set has no rows.
Use an if condition so in the event that SUM() yields NULL then have the value returned as 0 so you never have NULL - x or x - NULL or NULL - NULL.
DECLARE
v_in auto_service.vin%TYPE;
v_first auto_service.service_date%TYPE;
v_last auto_service.service_date%TYPE;
v_max auto_service.price%TYPE;
v_total auto_service.price%TYPE;
v_n NUMBER;
CURSOR c_auto
IS
SELECT vin,
COUNT(*) AS no,
MIN(SERVICE_DATE) AS FIRSTprice,
MAX(SERVICE_DATE) AS lastprice,
max(price) as maxprice,
sum(price) as totalprice
FROM auto_service
GROUP BY vin;
BEGIN
OPEN c_auto;
FETCH c_auto INTO v_in,v_n,v_first,v_last,v_total,v_max;
IF c_auto%notfound THEN
dbms_output.put_line('No output');
ELSE
dbms_output.put_line('vin No firstprice lastprice maximumprice totalprice');
LOOP
dbms_output.put_line(rpad(v_in,10) || rpad(v_n,10) || rpad(v_first,10) || rpad(v_last,12) || rpad(v_max,15) || rpad(v_total,5));
FETCH c_auto INTO v_in,v_n,v_first,v_last,v_max,v_total;
EXIT
WHEN c_auto%notfound;
END LOOP;
END IF;
CLOSE c_auto;
END;
To find
the number of services, the first service date and the price for the first service,the last service date and the price for the last service, the maximum price and the service date for the maximum price, and the prices for all services
I got all the other things except price for the first service date and last date of all VIN.
select q.*,
(
select sum(price)
from auto_service x
where x.vin = q.vin
and x.service_date = q.FIRST_DATE
) as FIRST_PRICE,
(
select sum(price)
from auto_service x
where x.vin = q.vin
and x.service_date = q.LAST_DATE
) AS LAST_PRICE
from
(
SELECT vin,
COUNT(*) AS no,
MIN(SERVICE_DATE) AS FIRST_DATE,
MAX(SERVICE_DATE) AS LAST_DATE,
max(price) as maxprice,
sum(price) as totalprice
FROM auto_service
GROUP BY vin
) q
Notice that I renamed the columns you named as FIRSTprice and lastprice to first_date and last_date, since this name is more related to what the column does actually contain.
in second place: i used "select sum(price)" in the subqueries only to handle the possibility that the same car has been serviced twice on the same day. if this should happen, without the sum(), the subquery would extract more than a value and would give you a runtime error. This much less likely to happen if if the date field contains also the time part, not only the date, but it could still happen if your DB contains bad data.
it is up to you if you want to keep that sum() call or if you prefer the db to reveal errors if there are duplicate rows you don't expect
i have payment table fields
update reason and amount & total field are change negative
UPDATE payment
SET reason = 'refund'
WHERE uid =5 AND date = '2012-05-01' AND accid =2
update single query is it possible?
If I understand you correctly, you also want to set amount column to positive value along with the above statement.
You can use something like this
UPDATE payment
SET reason = 'refund', amount = amount * -1, total = total * -1
WHERE uid =5 AND date = '2012-05-01' AND accid =2
Use ABS(amount) if you wish to always get the positive integer.
SELECT ABS(5);
will output 5
SELECT ABS(-5);
will also output 5
When I looked for the solution, the offered suggestion corrupted my result:
SELECT #TotalAmount:=( SELECT FORMAT(SUM(Amount), 4) FROM MPPayment WHERE PaymentBatchID = 6 and CompanyID=3);
Proper result:
After formatting:
SELECT #TotalAmount:=( SELECT FORMAT(SUM(Amount), 4) FROM MPPayment WHERE PaymentBatchID = 6 and CompanyID=3);
SELECT #TotalAmount * -1;
Probably doesn't work well with formatting.
Another solution is to subtract your digit from zero:
SELECT #TotalAmount:=( SELECT SUM(Amount) FROM MPPayment WHERE PaymentBatchID = 6 and CompanyID=3);
select FORMAT((0 - #TotalAmount), 4 );
To avoid the corruption of the the result I described above, I make formatting at the end of the operation. The result is fine then:
Works also with multiplication by -1:
SELECT #TotalAmount:=( SELECT SUM(Amount) FROM MPPayment WHERE PaymentBatchID = 6 and CompanyID=3);
select FORMAT(( #TotalAmount *-1), 4 );