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.
Related
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.
I have a database that has tables:
Click Internal - product clicks tracked by our system
Click External - product clicks tracked by a partner system. Number of clicks: click_internal >= click_external, also for each external click there is a column with bool click_external.click_valid
Conversion - purchases that happened after a click was tracked in partner system
Product - information about product that was clicked or bought
Producer - information about producer of the product
The database schema looks like this:
I would like to show number of valid external clicks and conversions for each producer for given date. So the result should look something like this:
So far I got:
SELECT
producer.name,
COUNT(click_external.id),
COUNT(conversion.id),
SUM(conversion.price)
FROM click_external
LEFT JOIN click_internal ON click_internal.id = click_external.click_internal_id
LEFT JOIN product ON product.id = click_internal.product_id
LEFT JOIN producer ON producer.id = product.producer_id
LEFT JOIN conversion ON click_internal.id = conversion.click_internal_id
WHERE click_external.click_valid = 1
AND click_external.date = "2017-02-09"
GROUP BY producer.id
-- GROUP BY click_external.id
The problem is when I try to group by producer.id the sum of COUNT(CLICK_EXTERNAL.id) column is greater than there are actually rows (clicks) in the DB. i.e. Sum of clicks by each provider is greater than number of ungrouped clicks.
I have also tried to group by click_external.id (as seen in the code comment) to see which rows are there. The number of rows in result is ok, but some rows are counted more times. i.e. COUNT(CLICK_EXTERNAL.id) column is greater than 1 at some rows.
What should I change in the SQL?
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()
I have a table "Log"
My game server inserts a record into this table when someone login the server, then inserts a second record when they logout.
What I want to do is create a query to count the number of people logged in.
the main data that gets inserted to the table "Log"
When they Login:
[Type] = 0
[Player1] = Their account ID
[Value2] = a random number which matches the logout row when they logout
[Value3] = 0
When they Logout:
[Type] = 1
[Player1] = Their account ID
[Value2] = a random number which matches the login row when they logout
[Value3] = some random number
Is there a way I can count the last "Player1" of each account and check if "Type" = 0 which means that account is logged in then echo the result.
The result I'm looking for would pull the last record of every account an count them.
Note: everytime an account logs in and out it inserts them 2 records so if 1 account logs in 20 times there would be 40 records in "Log"
One way to do it is to count all rows with type 0 for which there doesn't exist any type 1 row with the same player and a later date:
select count(*) as number_of_logged_in
from log l
where Type = 0 -- 0 meaning log on event
-- and [Value3] = 0 -- maybe this should be included
and not exists (
select 1 from log
where Player1 = l.Player1
and type = 1 -- 1 meaning log out event
and date > l.date
-- and [Value2] = l.[Value2] -- maybe this should be included
);
I found your problem statement a bit confusing as you say you want to count the number of people that are logged in, but then you say I want to count the last of each [Player!] where [Type] is 1 which seems to be the opposite. It's also not clear to me why the random number would be important - if the last recorded type for a user is 0 then they should be considered as logged in, or?
Sample SQL Fiddle with some demo data
I am assuming you want list of the all the logged in players names,so you can try using the ROW_NUMBER() to get what you want,
;WITH CTE AS(
SELECT
Player1 AS LoggedInPlayer,
ROW_NUMBER() OVER (PARTITION BY Player1 ORDER BY datecolumn Asc) As LoggedValue
FROM
yourtable
)
SELECT
*
FROM
CTE
WHERE
LoggedValue = 1
If you know, that all logins and logouts are stored in Log without gaps, you can simply count them and if there's a difference you know, that the player is currently logged in.
SELECT logins.player1, logouts.cnt - logins.cnt
FROM
(select player1, count(*) as cnt from Log where type = 0 group by player1) as logins
LEFT OUTER JOIN
(select player1, count(*) as cnt from Log where type = 1 group by player1) as logouts
ON (logins.player1 = logouts.player1)
WHERE logins.cnt > logouts.cnt or logouts.player1 is null
You need the left outer join, if the player logged in one time and never logged out. Sorry, if you encounter syntax issues. I just wrote this without testing and usually work on a Teradata System and the SQL Dialect there. But as the SQL given here is plain Ansi, it should work on any database.
I have been trying many different ways to solve this problem from this forum and from many others. I can't seem to find a solution to this problem or any documentation that will give me a straight answer.
I wondered if you could have a look at it for me.
Thanks
THE PROBLEM:
I've got a database with the following tables
participant_scores
leagues
rounds
I am currently able to display the scores of a single round, one round at a time... which is exactly how I want it.
But I also want to display the score that each participant got for all rounds.
Lets say we have 2 rounds. I want the output on my results screen to look something like this:
Currently viewing league 20, Round 1
of 2:
User Name | Score | Total Score
Tom | 10 | 200
James | 50 | 300
username - the participant's name
score = the score for this current round
total score = all of the round scores for this league added together.
My Mysql query is below. Apologies for the messyness of it, I've rewritten it about 100 times and this current way is the only way that works fully.
>> league_participants_query (mysql)
# FIELDS
SELECT
participants.participant_id, # ID - used for functions
participants.participant_name, # NAME
participants.participant_gender, # Participant info
classes.class_name, # Class name
schools.school_name, # School name
participant_scores.participant_score, # Participant score
participant_scores.participant_score_id
# TABLES
FROM participant_scores, participants, classes, league_schools, schools, leagues, rounds
# filter leagues
WHERE leagues.league_id = 51
AND rounds.league_id = 51 # the current league we are viewing
AND rounds.round_id = 25 # the current round of the league we are viewing
# filter league schools
AND participant_scores.round_id = 25 # the current round of the league we are viewing
# filter schools allowed
AND league_schools.league_id = 51 # the current league we are viewing
# Filter schools
AND schools.school_id = league_schools.school_id
# Filter classes
AND classes.school_id = schools.school_id
AND classes.year_group_id = leagues.year_group_id
# Filter participants
AND participants.class_id = classes.class_id
# Filter participant_scores
AND participant_scores.participant_id = participants.participant_id
#Grouping
GROUP BY participants.participant_id
What you want here is a subquery in this form:
SELECT
name,
round,
score,
( select sum( score ) from scores sc where sc.userid = users.userid ) total
FROM users INNER JOIN scores on users.userid = scores.scoreid
The subquery as a column will be calculated for each row you return from your initial query.
To try to add it into your query:
SELECT
participants.participant_id,
participants.participant_name,
participants.participant_gender,
classes.class_name,
schools.school_name,
participant_scores.participant_score,
participant_scores.participant_score_id,
( SELECT sum(participant_score) FROM participant_scores tbl_scores2
WHERE tbl_scores2.participant_score_id = participants.participant_id ) total
FROM participant_scores, participants, classes,
league_schools, schools, leagues, rounds
WHERE
leagues.league_id = 51 AND
rounds.league_id = 51 AND
rounds.round_id = 25 AND
participant_scores.round_id = 25 AND
league_schools.league_id = 51 AND
schools.school_id = league_schools.school_id AND
classes.school_id = schools.school_id AND
classes.year_group_id = leagues.year_group_id AND
participants.class_id = classes.class_id AND
participant_scores.participant_id = participants.participant_id
GROUP BY
participants.participant_id
I was a little worried about the subquery including multiple leagues, but it looks like a single participant can only belong to one league anyway. You might need to include something in the subquery to check for this.