How Implement a system to determine if a milestone has been reached - database

I have a table named stats
player_id team_id match_date goal assist`
1 8 2010-01-01 1 1
1 8 2010-01-01 2 0
1 9 2010-01-01 0 5
...
I would like to know when a player reach a milestone (eg 100 goals, 100 assists, 500 goals...)
I would like to know also when a team reach a milestone.
I want to know which player or team reach 100 goals first, second, third...
I thought to use triggers with tables to accumulate the totals.
Table player_accumulator (and team_accumulator) table would be
player_id total_goals total_assists
1 3 6
team_id total_goals total_assists
8 3 1
9 0 5
Each time a row is inserted in stats table, a trigger will insert/update player_accumulator and team_accumulator tables.
This trigger could also verify if player or team has reached a milestone in milestone table containing numbers
milestone
100
500
1000
...
A table player_milestone would contains milestone reached by player:
player_id stat milestone date
1 goal 100 2013-04-02
1 assist 100 2012-11-19
There is a better way to implements a "milestone" ?
There is an easiest way without triggers ?
I'm using PostgreSQL

I'd just count all goals and assists of a player which scores, and team, which scores.
Like this on client side (in pseudocode):
function insert_stat(player_id, team_id, match_date, goals, assists)
{
if (goals>0) {
player_goals_before = query('select count(goal) from stats where player_id=?',player_id);
team_goals_before = query('select count(goal) from stats where team_id=?',team_id);
}
if (assists>0) {
player_assists_before = query('select count(assist) from stats where player_id=?',player_id);
team_assists_before = query('select count(assist) from stats where team_id=?',team_id);
}
query("insert into stats (player_id, team_id, match_date, goal, assist)\n"
+"values (?, ?, ?, ?, ?)", player_id, team_id, match_date, goal, assist);
if (goals>0) {
if ( has_milestone(player_goals_before+goals) and !has_milestone(player_goals_before) ) {
alert("player " + player_id + " reached milestone!")
}
if ( has_milestone(team_goals_before+goals) and !has_milestone(team_goals_before) ) {
alert("team " + team_id + " reached milestone!")
}
}
// etc
}
Do not maintain milestone table, as this makes the database denormalized. I think this is a premature optimization. Only when the above is really not fast enough (for example when stats will have more than few thousands of rows per player_id or team_id) then you can think of maintaining milestone table.

Related

Display a dynamically updated quantity BUT store the older versions

I'm working on a web application for a farmer, a permacultor to be more precise. It implies that he's handling various seeds and plants. The purpose of the application is to store in database data regarding his garden. Once enough data are gathered, the application is to provide a frame to analyze data.
For now I'm developping the first fonctionality : store data in database and display them on webpages.
Let's focus on the main topic of this question.
The garden has several fields. A field can contain several plants. Plants can have several state throught time (seed, plant, flower, harvestable).
When a plant reach a state, we need to store specific information :
the state
the date when state was observed (and other time-related data)
the quantity (from seed state to flower state, we can have loss for many reasons)
So far so good, nothing fancy.
NOW a plant can grow on a field until a specitifc state then be planted into another field until its harvesting.
For instance, 12 carrots that are growing in tray n°3 from the seed to germination state.
At germination state, 2 carrots didn't make it. The farmer now intend to resume the growing of his carrots not in tray n°3 but in field n°1
In model, let's say "state_plant_table" you have 2 entries :
carrots - 12 - seeds - tray n°3
carrots - 10 - germ - field n°1
You might see it coming.
Let's say now that... there isn't enough room in field n°1 for the 10 carrots, only 8 can fit. So he just put the 2 left in the field aside - field n°2.
We now have
carrots - 12 - seeds - tray n°3
carrots - 8 - germ - field n°1
carrots - 2 - germ - field n°2
NOW, on display we would show HTML table for each fields, trays or w/e. When you click on a field you have the detail of every plants rooted in it.
For field n°1 we would have :
carrots - 8
For field n°2 we would have :
carrots - 2
And, unfortunately, for tray n°3, we would have :
carrots - 12
But we should have 0 (if 0 => exclude from display of course).
I'm struggling with the theorical design of my process right now... any tips, hints, suggestions are welcome !
I have thought about a "parent" quantity and a "child" quantity where the initial quantity would be store in "plant_table" as "parent" quantity and "children" quantity would be stored in "state_plant_table" - the quantity is more linked to a state in which it's being observed than the plant itself.
I feel like this is the right way, but I don't manage to push the reasoning to its end either.
Reasoning with "parent" and "children" was one of the correct approach.
There are actually 3 natures of quantity to store :
quantity of plants observed at a certain state (quantite_etat)
quantity of plants actually in the field (quantite)
quantity of plants from the same parent (quantite_lot)
models.py
class EtatLot(models.Model):
id = models.AutoField(primary_key = True)
id_lot = models.ForeignKey('Lot', on_delete = models.PROTECT, verbose_name = 'Lot', related_name = 'lot_parent', null = True, blank = True)
etat = models.CharField(verbose_name = 'État', max_length = 50, null = True, blank = True)
quantite = models.PositiveSmallIntegerField(verbose_name = 'Quantité', null = True, blank = True)
quantite_etat = models.PositiveSmallIntegerField(verbose_name = 'Quantité relatée', null = True, blank = True)
class Lot(models.Model):
id = models.AutoField(primary_key = True)
quantite_lot = models.PositiveSmallIntegerField(verbose_name = 'Quantité', null = True, blank = True)
The quantity displayed is the one from quantite. The quantity used to analyze data is the one from quantite_etat.
Example
Let's say we have 12 cauliflower on field n°1 and the farmer want to plant 10 of them on field n°2.
Within database we have :
Lot table
id : 1 quantite_lot : 12
EtatLot table
id : 1 id_lot : 1 etat : Seed quantite : 12 quantite_etat : 12
id : 2 id_lot : 1 etat : Germ quantite : 12 quantite_etat : 12
At the end of operations, we should have this :
Lot table
id : 1 quantite_lot : 2
EtatLot table
id : 1 id_lot : 1 etat : Seed quantite : 12 quantite_etat : 12 field : fieldn°1
id : 2 id_lot : 1 etat : Germ quantite : 2 quantite_etat : 12 field : fieldn°1
id : 3 id_lot : 1 etat : Plan quantite : 10 quantite_etat : 10 field : fieldn°2
For this operation, quantite_lot is irrelevant. However I store it in order to do some stock check : you cannot plant more plants that you have.
This is how I achieved to the table above :
get quantity from the last child of lot_parent (quantite)
update this value with the difference between it and the value added by the farmer in the form, update the value from quantite_lot in the parent as well
store the value of the form in quantite and quantite_etat of the entry that is about to be added in the database

Add incremental number in duplicate records

I have SSIS package, which retrieves all records including duplicates. My question is how to add an incremental value for the duplicate records (only the ID and PropertyID).
Eg
Records from a Merge Join
ID Name PropertyID Value
1 A 1 123
1 A 1 223
2 B 2 334
3 C 1 22
3 C 1 45
Now I need to append an incremental value at the end of the each record as
ID Name PropertyID Value RID
1 A 1 123 1
1 A 1 223 2
2 B 2 334 1
3 C 1 22 1
3 C 1 45 2
Since ID 1 & 3 are returned twice, the first record has RID as 1 and the second record as 2.
ID and PropertyID need to be considered to generate the Repeating ID i.e RID.
How can I do it in SSIS or using SQL command?
Update #1:
Please correct me if I'm wrong, since the data is not stored in any table yet, I'm unable to use the select query using rownumber(). Any way I can do it from the Merge Join?
You could use ROW_NUMBER:
SELECT ID,
Name,
PropertyID,
Value,
ROW_NUMBER() OVER(PARTITION BY ID, PropertyID ORDER BY Value) As RID
FROM TableName
This will do the job for you: https://paultebraak.wordpress.com/2013/02/25/rank-partitioning-in-etl-using-ssis/
You will need to write a custom script, something like this:
public
class
ScriptMain : UserComponent
{
string _sub_category = “”;
int _row_rank = 1;
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
if (Row.subcategory != _sub_category)
{
_row_rank = 1;
Row.rowrank = _row_rank;
_sub_category = Row.subcategory;
}
else
{
_row_rank++;
Row.rowrank = _row_rank;
}
}
}

Removing the repeating elements from a row in a squlite table

Please let me know if there is any query where in I remove the repeating entries in a row.
For eg: I have a table which has name with 9 telephone numbers:
Name Tel0 Tel1 Tel2 Tel3 Tel4 Tel5 Tel6 Tel7 Tel8
John 1 2 2 2 3 3 4 5 1
The final result should be as shown below:
Name Tel0 Tel1 Tel2 Tel3 Tel4 Tel5 Tel6 Tel7 Tel8
John 1 2 3 4 5
regards
Maddy
I fear that it will be more complicated to keep this format than to split the table in two as I suggested. If you insist on keeping the current schema then I would suggest that you query the row, organise the fields in application code and then perform an update on the database.
You could also try to use SQL UNION operator to give you a list of the numbers, a UNION by default will remove all duplicate rows:
SELECT Name, Tel FROM
(SELECT Name, Tel0 AS Tel FROM Person UNION
SELECT Name, Tel1 FROM Person UNION
SELECT Name, Tel2 FROM Person) ORDER BY Name ;
Which should give you a result set like this:
John|1
John|2
You will then have to step through the result set and saving each number into a separate variable (skipping those variables that do not exist) until the "Name" field changes.
Tel1 := Null; Tel2 := Null;
Name := ResultSet['Name'];
Tel0 := ResultSet['Tel'];
ResultSet.Next();
if (Name == ResultSet['Name']) {
Tel1 := ResultSet['Tel'];
} else {
UPDATE here.
StartAgain;
}
ResultSet.Next();
if (Name == ResultSet['Name']) {
Tel2 := ResultSet['Tel'];
} else {
UPDATE here.
StartAgain;
}
I am not recommending you do this, it is very bad use of a relational database but once implemented in a real language and debugged that should work.

Modeling a non-primary Key relationship

I am trying to model the following relationship with the intent of designing classes for EF code first.
Program table:
ProgramID - PK
ProgramName
ClusterCode
Sample data
ProgramID ProgramName ClusterCode
--------------------------------------
1 Spring A
2 Fall A
3 Winter B
4 Summer B
Cluster table:
ID
ClusterCode
ClusterDetails
Sample data:
ID ClusterCode ClusterDetails
---------------------------------
1 A 10
2 A 20
3 A 30
4 B 20
5 B 40
I need to join the Program table to the Cluster table so I can get the list of cluster details for each program.
The SQL would be
Select
from Programs P
Join Cluster C On P.ClusterCode = C.ClusterCode
Where P.ProgramID = 'xxx'
Note that for the Program table, ClusteCode is not unique.
For Cluster table, neither ClusterCode nor ClusterDetail is unique.
How would I model this so I can take advantage of navigation properties and code-first?
assuming you have mapped above two tables and make an association between them and you are using C#, you can use a simple join :
List<Sting> clustedDets=new ArrayList<String>();
var q =
from p in ClusterTable
join c in Program on p equals c.ClusterTable
select new { p.ClusterDetails };
foreach (var v in q)
{
clustedDets.Add(v.ClusterDetails);
}

Querying table for multiple combinations and default values

I have a table like
City POSTAGE PRICE**
HOUSTON DEFAULT 20
DEFAULT AIR 14
DEFAULT GROUND 30
DEFAULT DEFAULT 40
Now i want to query for price on this table with a combination like 'CHICAGO,GROUND'
which should check if the perfect combination exists, else i should substitute DEFAULT and search for the value..
example,
HOUSTON,AIR should return 14
HOUSTON,GROUND should return 20
HOUSTON,FEDEX should return 20
CHICAGO,FEDEX should return 40
Is there a way to achieve this instead of writing multiple queries ..
thank you!
This uses the SQL*Plus syntax for passing parameters, you may need to change that to suit:
select price
from your_table
where ( city = '&p_city' or city = 'DEFAULT')
and ( postage = '&p_postage' or postage = 'DEFAULT')
order by case when city = '&p_city' then 1 else 2 end
, case when postage = '&p_postage' then 1 else 2 end
This will return multiple rows but presumably you want only the one PRICE. The ORDER BY clause prioritises matches on CITY over matches on POSTAGE. You can then select the first row.

Resources