Mysql query in django view - mysql

I do not have much knowledge of mysql. My django model is as follows:
class Exercise_state(models.Model):
exercise = models.ForeignKey(Exercise, blank=True, null=True)
user = models.ForeignKey(User, blank=True, null=True)
intensity_level = models.IntegerField(default='1')
progress = models.IntegerField(default='0')
current_date = models.DateField(auto_now=True)
user_rating = models.IntegerField(default='0')
I want to retrieve count of user_id's which satisfy exercise_id=1 corresponding intensity_level=7 and so on for all exercise_id's till 7. In short, users who have reached intensity_level=7 in all the exercises.
I have written one query which is as follows:
select count(user_id) from demo_exercise_state where
exercise_id=1 and intensity_level=7 and
exercise_id=2 and intensity_level=7 and
exercise_id=3 and intensity_level=7 and
exercise_id=4 and intensity_level=7 and
exercise_id=5 and intensity_level=7 and
exercise_id=6 and intensity_level=7 and
exercise_id=7 and intensity_level=7;
As far as I can cross check results from my database directly no user has yet completed his training (i.e. who has reached intensity_level=7 in all exercise types), so it returns the count as '0'.
I am sure this can query be optimized as there is quite much repeatation, but I am not sure how.
Also I want to execute the same (i.e. get the same result) in my django view. There I have something like
all_exercise_finish_count = Exercise_state.objects.filter(exercise_id=1, intensity_level=7).count()
How can I reflect the same in this django view?

Update
Completely changing my answer because the subsequent discussion in the comment made it clear that the OPs intentions were quite different from what it first appeared to be.
You can find the users who have completed all the 7 excercises at intensity 7 in the following way
from django.db.models.import Count
Exercise_state.objects.annotate(
c = Count('exercise').filter(c=7, exercise_id= 7)
First form of the answer is below:
The original raw query can be greatly simplified with between
expr BETWEEN min AND max
If expr is greater than or equal to min and expr is less than or equal
to max, BETWEEN returns 1, otherwise it returns 0. This is equivalent
to the expression (min <= expr AND expr <= max) if all the arguments
are of the same type. Otherwise type conversion takes place according
to the rules described in Section 13.2, “Type Conversion in Expression
Evaluation”, but applied to all the three arguments.
select count(user_id) from demo_exercise_state where
exercise_id BETWEEN 1 and 7 AND intensity_level=7
And the django query can simply be
all_exercise_finish_count = Exercise_state.objects.filter(exercise_id__gte=1, intensity_level=7, excercise_id__lte=7).count()

Related

Extract the number of invoices of the current year in mysql

Goodmorning everyone.
I'm going crazy
I need to extract the number of invoices from the current year per customer.
in my table tbl_preventivi I have the field anagrafica_id for the customer and date_prev for the date of the invoice.
this is my code.
$anno = date('Y');
SELECT tbl_preventivi.anagrafica_id, Count(tbl_preventivi.preventivo_id) AS totale
FROM tbl_preventivi
GROUP BY tbl_preventivi.anagrafica_id, Year(tbl_preventivi.data_prev)
HAVING ((Year(tbl_preventivi.data_prev) = ".$anno.") AND (tbl_preventivi.anagrafica_id=".$_GET['anagrafica_id']."))
i am sure that in the test i am doing the result must be 1, instead the query is null.
if I remove
(Year(tbl_preventivi.data_prev) = ".$anno.") AND the query works and returns 6 (which is the number of invoices made even in previous years).
where am i wrong?
The simplest solution would be something like:
SELECT p.`anagrafica_id`,
COUNT(p.`preventivo_id`) as `totale`
FROM `tbl_preventivi` p
WHERE p.`anagrafica_id` = 12345 and YEAR(p.`data_prev`) = 9999
GROUP BY p.`anagrafica_id`;
Note: 12345 should be replaced with a sanitized customer number, and 9999 should be replaced with a sanitized and logical year. Prepared statements are your friend here.
If you wish to see all invoices for a customer across all years, you can do this:
SELECT p.`anagrafica_id`,
YEAR(p.`data_prev`) as `year`,
COUNT(p.`preventivo_id`) as `totale`
FROM `tbl_preventivi` p
WHERE p.`anagrafica_id` = 12345
GROUP BY p.`anagrafica_id`, `year`
ORDER BY `year` DESC;
Again, be sure to sanitize your inputs. In the famous words of Fox Mulder: Trust no one.

Update SQL query in flask not working with floating/decimal points

So, fairly new in here. Im trying to build a web application that simulates the act of "buying shares".
Everything ok, getting the value, working with it until i need to update the value of cash (the money the user has to spend). So, when I buy a stock, it shows the correct price, but when subtracting that value it rounds up on the SQL table.
This is the part of the code that updates the values. I can't get it to show 2 floating points.
#app.route("/")
#login_required
def index():
"""Show portfolio of stocks"""
# Get the user ID
user_id = session.get('user_id', None)
# Get the symbol. Query first on stocks SQL
dictionary = db.execute("SELECT symbol, SUM(shares), company FROM buy_stocks WHERE userid=(:userid) GROUP BY company", userid = user_id)
# Get the number to iterate on the HTML template
iterate = len(dictionary)
symbol = [None] * len(dictionary)
(continues and in the end:)
for i in range(len(dictionary)):
value = int( 100 * price[i] * shares[i] )
value_temp = value / 100
total[i] = value_temp
# Cash still avaiable for more transactions. Sum the total and subtract to the money the user still has
cash_query = db.execute("SELECT cash FROM users WHERE id=(:userid)", userid = user_id)
cash = cash_query[0]["cash"]
The solution I found for this was to extract the values to another function, and workk them there, forcing them to have 2 decimal places.
That did the trick, however, still trying to figure out how can't they be worked from the main function itself.

SQL - Add To Existing Average

I'm trying to build a reporting table to track server traffic and popularity overall. Each SID is a unique game server hosting a particular game, and each UCID is a unique player key connecting to that server.
Say I have a table like so:
SID UCID AvgTime NumConnects
-----------------------------------------
1 AIE9348ietjg 300.55 5
1 Po328gieijge 500.66 7
2 AIE9348ietjg 234.55 3
3 Po328gieijge 1049.88 18
We can see that there are 2 unique players, and 3 unique servers, with SID 1 having 2 players that have connected to it at some point in the past. The AvgTime is the average amount of time those players spent on that server (in seconds), and the NumConnects is the size of the average (ie. 300.55 is averaged out of 5 elements).
Now I run a job in the background where I process a raw connection table and pull out player connections like so:
SID UCID ConnectTime DisconnectTime
-----------------------------------------
1 AIE9348ietjg 90.35 458.32
2 Po328gieijge 30.12 87.15
2 AIE9348ietjg 173.12 345.35
This table has no ID or other fluff to help condense my example. There may be multiple connect/disconnect records for multiple players in this table. What I want to do is add to my existing AvgTime for each SID these new values.
There is a formula from here I am trying to use (taken from this math stackexchange: https://math.stackexchange.com/questions/1153794/adding-to-an-average-without-unknown-total-sum/1153800#1153800)
Average = (Average * Size + NewValue) / Size + 1
How can I write an update query to update each ServerIDs traffic table above, and add to the average using the above formula for each pair of records. I tried something like the following but it didn't work (returned back null):
UPDATE server_traffic st
LEFT JOIN connect_log l
ON st.SID = l.SID AND st.UCID = l.UCID
SET AvgTime = (AvgTime * NumConnects + SUM(l.DisconnectTime - l.ConnectTime) / NumConnects + COUNT(l.UCID)
I would prefer an answer in MySql, but I'll accept MS SQL as well.
EDIT
I understand that statistics and calculations are generally not to be stored in tables and that you can run reports that would crunch the numbers for you. My requirement is that users can go to a website and view the popularity of various servers. This needs to be done in a way that
A: running a complex query per user doesn't crash or slow down the system
B: the page returns the data within a few seconds at most
See this example here: https://bf4stats.com/pc/shinku555555
This is a web page for battlefield 4 stats - notice that the load is almost near instant for this player, and I get back a load of statistics without waiting for some complex report query to return the data. I'm assuming they store these calculations in preprocessed tables where the webpage just needs to do a simple select to return back the values. That's the same approach I want to take with my Database and Web Application design.
Sorry if this is off topic to the original question - but hopefully this adds additional context that helps people understand my needs.
Since you cannot run aggregate functions like SUM and COUNT by themselves at the unit level in SQL but contained in an aggregate query, consider joining to an aggregate subquery for the UPDATE...LEFT JOIN. Also, adjust parentheses in SET to match above formula.
Also, note that since you use LEFT JOIN, rows with non-match IDs will render NULL for aggregate fields and this entity cannot be used in arithmetic operations and will return NULL. You can convert to zero with IFNULL() but may fail with formula's division.
UPDATE server_traffic s
LEFT JOIN
(SELECT SID, UCID, COUNT(UCID) As GrpCount,
SUM(DisconnectTime - ConnectTime) AS SumTimeDiff
FROM connect_log
GROUP BY SID, UCID) l
ON s.SID = l.SID AND s.UCID = l.UCID
SET s.AvgTime = (s.AvgTime * s.NumConnects + l.SumTimeDiff) / s.NumConnects + l.GrpCount
Aside - reconsider saving calculations/statistics within tables as they can always be run by queries even by timestamps. Ideally, database tables should store raw values.

SQL Script for Counting Records based on specific criteria and returning the Counts

I'm Trying to count records with specific criteria in my Database to ultimately produce some statistical reports.
My Tables and Fields are:
1- Import Table:bed_ID, unit_ID, mrn, acccount_num, sex, service_ID
2- Beds: bed_ID, unit_ID, bed_type_ID
3- Bed_Type: bed_type_ID, bed_type_description
4- Unit: unit_ID, unit_common_name, program_ID, total_beds...other fields that don't apply.
5- Service: service_ID, service_common_name, program_ID
6- Program: program_ID, program_common_name
I want to create a query that will give me a count of each Bed_Type_Description for each Unit. I also want to get each units total beds and calculate beds available but I'm sure I can figure that out if I get help with this part.
Unit Regular_Bed Escalation_Bed Transfer_Bed Bassinet
-------------------------------------------------------------
Unit1 10 4 2 2
Unit2 12 2 2 0
etc...
etc...
This is what I have, but its only related to one specific Unit:
SELECT
COUNT(dw_test.dbo.Bed_Type.bed_type_description) as 'Total Number of Beds'
FROM
dw_test.dbo.MediTechReport_Bed_Board,
--dw_test.dbo.Unit,
dw_test.dbo.Beds,
dw_test.dbo.Bed_Type
WHERE
dw_test.dbo.MediTechReport_Bed_Board.bed_ID = dw_test.dbo.Beds.bed_ID
--AND
-- dw_test.dbo.MediTechReport_Bed_Board.unit_ID = dw_test.dbo.Unit.unit_ID
AND
dw_test.dbo.Beds.bed_type_ID = dw_test.dbo.Bed_Type.bed_type_ID
AND
dw_test.dbo.MediTechReport_Bed_Board.unit_ID = 'KA2MED'
AND
dw_test.dbo.Bed_Type.bed_type_description = 'Regular';
You'll notice a couple lines related to Unit that are commented out. With these commented out I get a returned value of '5' records which is correct. If i remove the commenting to include these lines it returns a value of '0' which makes no sense to me. If someone can explain this to me that'd be great as well.
My SQL is quite rusty, its been a while. Any assistance is greatly appreciated. Thanks again in advance for all your help.
Maybe something like this?
select t.bed_type_description, COUNT(t.bed_type_description) as 'Total Number Of Beds'
from import As I inner join Beds as b on i.bed_Id = b.bed_Id
inner join Unit as u on i.unit_Id = u.unit_Id
inner join Bed_Type as t on b.bed_type_Id = t.bed_type_Id
where u.unit_Id = 'KA2MED'
group by t.bed_type_description

How can I return a row for each date, even when there is no data for that date (in which case the row should be filled with zero's)?

I hope I will be able to make my problem clear.
Ik have a table called tweets from which I want to extract information for each data in the daterange table. This table holds 142 dates, of which 102 dates have the property trading (day on which market was open) set to 1 (trading=1).
The below query extracts information from the tweets table for 20 companies (identified by sp100_id). The expected resultset therefore contains 20 x 102 = 2,040 rows. However, I only get returned 1,987 rows because for some date-company combinations, the tweets table holds no data. I need these "empty days" to be included in the resultset however. I thought I could accomplish this by using COALESCE(X, 0), returning 0 if there would be no data, but the result is the same: 1,987 rows.
Based on this information and the query below, does anybody know how I can get it to return 102 rows (1 row for each daterange._date with trading=1) for each sp100_id in the tweets table?
SELECT
sp100.sp100_id,
daterange._date,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`retweet_count`, 0)),0) AS `pos-retweet`,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`user-quality`, 0)),0) AS `pos-quality`,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`follow`, 0)),0) AS `pos-follow`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`retweet_count`, 0)),0) AS `neg-retweet`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`user-quality`, 0)),0) AS `neg-quality`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`follow`, 0)),0) AS `neg-follow`
FROM
sp100
CROSS JOIN
daterange
LEFT JOIN
tweets
ON tweets.nyse_date = daterange._date
AND tweets.sp100_id = sp100.sp100_id
WHERE sp100.sp100_id BETWEEN 1 AND 20 AND tweets.type != 1 AND daterange.trading = 1
GROUP BY
sp100.sp100_id, daterange._date
In any other case, I would provide you with a SQLFiddle, but it would be a lot of work to export a proper portion of the tables used to SQLFiddle while the solution might be clear to some real SQL guru anyway :-)
The problem comes from requiring that tweets.type != 1 in your WHERE clause.
For the dates that have no associated tweets, the outer join will result in all tweets columns, including tweets.type, being NULL. As documented under Working with NULL Values:
Because the result of any arithmetic comparison with NULL is also NULL, you cannot obtain any meaningful results from such comparisons.
In MySQL, 0 or NULL means false and anything else means true. The default truth value from a boolean operation is 1.
Therefore such records are filtered by your WHERE clause.
As #Martin Smith commented, you can move this filter criterion into the ON clause of your outer join (so that the test is performed only against actual tweets records rather than simulated NULL ones).
Alternatively, you could rewrite the filter to handle NULL. For example, using the NULL-safe equality operator:
NOT tweets.type <=> 1
As an aside, I usually don't bother with a daterange table and instead omit dates for which there is no data from the resultset: instead, I handle missing dates within my application code.
You need a calendar table filled with each day. I know it might sound silly, but this solution solves yo a lot of problems. The same solution you can have also with integers ( integer tables)