PHP and MySQL average - php

I have this structure in table named grades
1st row: 2nd row:
------------------------------
| english | math |
------------------------------
| 3 | 4 |
| 4 | 4 |
| 5 | 2 |
| 2 | 5 |
------------------------------
How do I now calucate average of english row?
I tried with:
"SELECT AVG(english) as `averageenglish` FROM grades"
It always gives me "No database selected"

Database selections are per session. If you go into the MySQL command prompt and type
USE MyDatabase
you do not have to qualify table names in queries for that session. As soon as you leave (exit, Ctrl+c, etc.) you don't have that privilege. You will have to USE the database again or qualify table names in queries. This includes a session created by a separate script that is not running on the command line.
In production code, it is helpful to always qualify table names in queries. Your query should look like:
SELECT AVG(english) as averageenglish FROM MyDatabase.grades

To demonstrated what #eggyal means
SELECT AVG(english) as `averageenglish` FROM databasename.grades
The preferred method is to add the database name to your connection if you only have one database.

Related

SQL QUERY how to select column by other constraints

I am using backpack-crud controller for
PHP-Laravel.
With the crudController given by backpack (library), all I have to do
is to query it with Laravel Eloquent (also raw sql is possible) queries.
Than the Backpack library will automatically
print the listview for me.
But I am struggling with this difficult query.
The thing is that I have 4 columns,
session_id | column_id | batch | data
10 | 1 | 1 | data1
10 | 2 | 1 | data2
10 | 1 | 2 | data1*
10 | 2 | 2 | data2*
Let's say this is the data I have.
I want to display this grouping by session_id, batch,
and ordering within row by column_id.
so the result query would be something like
1 : data1 data2
2 : data1* data2*
If there is a third batch with data
session_id | column_id | batch | data
10 | 1 | 3 | data1**
Then it would appear under the third batch as
3 : data1**
I can do this with code but not with sql.
Any advice would be grateful.
This looks like a PIVOT in sql server. Unfortunately mysql does not have this feature.
I can give you an approximate raw mysql query using GROUP_CONCAT. Assuming your table name is mytable.
SELECT
session_id,
batch,
GROUP_CONCAT(data ORDER BY column_id SEPARATOR ', ') AS dataList
FROM mytable
GROUP BY session_id, batch
Then you can split the aliased dataList column using given separator (here I've used ,).
You may change the separator according to the data contain in data column as you wish.
Hope this helps to you.

Proper way to create a sequence based on multiple fields

I'm using Laravel and Migrations to build my entire database structure.
Problem description
In schema, I have a pack table, that belongs to user and group and need to keep a kind of unique "index" for each different combination of these tables.
It means: a sequential number that increments based on distinct user_id and group_id. For example:
| id | user_id | group_id | sequence |
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 1 | 3 | 1 |
| 4 | 1 | 1 | 2 |
| 5 | 1 | 2 | 2 |
| 6 | 1 | 3 | 2 |
| 7 | 2 | 1 | 1 |
| 8 | 2 | 2 | 1 |
| 9 | 2 | 3 | 1 |
This will be used to references a pack on view layer:
user 1, this is your pack 1 of group 1.
user 1, this is your pack 2 of group 1.
user 1, this is your pack 1 of group 2.
I designed my migration (on up) like:
Schema::create('pack', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('user');
$table->integer('group_id')->unsigned();
$table->foreign('group_id')->references('id')->on('group');
$table->integer('sequence')->unsigned();
});
And use this business logic to fill $pack->sequence field on model layer.
Question 1:
Theoretically, this should be considered the best strategy to use in described scenario?
Question 2:
There's some pattern/approach that can be used to fill sequence field on database layer?
It appears you already have an auto-increment column id. MySQL does not support more than one auto-increment column per table.
In general, you can't get the behavior you're describing while allowing concurrent inserts to the table. The reason is that you have to read the max sequence value for some user/group pair, then insert the next value as you insert a new row.
But this creates a race condition, because some other concurrent session could be doing the same thing, and it will sneak in and insert a row with the next sequence value in between your session's steps of reading and inserting.
The solution is to use locks in a way to prevent a concurrent insert of the same user_id and group_id. InnoDB will use gap locks to help this.
Example:
Open two MySQL clients. In the first session, try this:
mysql> begin;
mysql> select max(sequence) from pack where user_id=1 and group_id=1 for update;
+---------------+
| max(sequence) |
+---------------+
| 2 |
+---------------+
The FOR UPDATE locks the rows examined, and it locks the "gap" which is the place where other rows with the same user_id and group_id would be inserted.
To prove this, try in the second session:
mysql> begin;
mysql> insert into pack set user_id=1, group_id=1, sequence=3;
It hangs. It can't do the insert, because that conflicts with the gap lock still held by the first session. The race-condition has been avoided.
Now in the first session, finish the work.
mysql> insert into pack set user_id=1, group_id=1, sequence=3;
mysql> commit;
Notice after the commit, immediately session 1's locks are released. The second session resolves its blocked INSERT, but it correctly gets an error:
ERROR 1062 (23000): Duplicate entry '1-1-3' for key 'user_id'
Of course, session 2 should have done the same SELECT...FOR UPDATE. That would have also been blocked until it could resolve the lock conflict. Once it resolved, it would have returned the correct new max sequence value.
The locks are only per user_id/group_id combo, if and only if you have a suitable index. I used:
ALTER TABLE pack ADD UNIQUE KEY (user_id, group_id, sequence);
Once you have that key, the SELECT...FOR UPDATE is able to be specific to the right set of rows when it locks them.
What this means is that even if user_id=1, group_id=1 is locked, you can still insert a new entry for any other values of user_id or group_id. They lock distinct parts of the index, so there's no conflict.
I encourage you to do some experiments yourself to prove to yourself you understand how it works. You can do this without writing any PHP code. I just opened two Terminal windows, ran the mysql command-line client, and started writing at the mysql> prompt. You can too!

Is it possible to reference a MySQL table dynamically?

I can't find anything about dynamically referencing a MySQL table entry in my particular case. Most everything I've read leans towards it not being possible, but I'm hoping someone can prove me wrong.
Essentially, I've got multiple MySQL tables that I'm trying to pull data from on an Android app. I want to access 2 at a time. The 1st Table's name always stays the same, history. The 2nd Table's name, however, may be different at times. It's value is determined within the app and referenced with :job in my php script (I'll use moon for my example). The 2nd table itself is generated dynamically through the app, so I guess I'm trying to set up a reference within a php script I have saved to a server so that I can access the 2nd Table.
Sorry for the confusing description, I hope these tables will help explain what I'm trying to get at.
Table #1: history (always stays the same)
| site | code | hours|
|---------|---------|------|
| moon | first | 1 |
| moon | second | 2 |
| moon | third | 3 |
| earth | fourth | 4 |
Table #2: moon (this one I want to dynamically reference)
| code | hours|
|---------|------|
| first | 10 |
| second | 11 |
| third | 12 |
And my current code:
...
/*** Table #1 ***/
SELECT code,
SUM(hours) AS total, '' AS target
FROM history
WHERE site = :job /* :job ends up being moon in this example */
GROUP BY code
UNION ALL
/*** Table #2 ***/
SELECT code,
'' AS total, SUM(hours) AS target
FROM :job /* <--- I'm trying to do something along these lines and use 'moon', or 'earth', or whatever... */
GROUP BY code
...
And later I get :job from the app: (moon)
$query_params = array(
':job' => $_POST['jobname'],
);
Result I'm Looking For: (works perfect if I directly use Table #2's name (ie moon) in my php file)
| code | hours|target|
|---------|------|------|
| first | 1 | 10 |
| second | 2 | 11 |
| third | 3 | 12 |
The code absolutely works as expected when I replace the :job in the 2nd table with the actual name of the table. I'm wondering if there is some way to still do it dynamically though?
Thanks for any and all advice!
I've done some pretty extensive searching and haven't come up with anything that works for me.
Is it possible to reference a mysql table entry value from a second table entry dynamically?
https://dev.mysql.com/doc/refman/5.7/en/derived-tables.html
MySQL table.* AS reference
Retrieve parent-child hierarchy from a self-referencing mysql table

PHP: Parse column name values from MySQL query statements?

I'm trying to build audit logging for a website. What I want to do is allow existing MySQL queries to go in unchanged (if possible) to a PHP function which parses the data and stores it in the audit log table.
For example, if I have the query
UPDATE members SET name='Bob', age='40' WHERE memberId=123
then I'd like to be able to pull out the table name members, the rowId 123 and both column/data (as if key/value) pairs name:Bob and age:40.
If possible I'd like a solution which also allows for the alternative query format:
UPDATE members (name,age) VALUES ('Bob','40') WHERE memberId=123
These would then go into an audit log table which would look something like this:
+--------+---------+--------+-----+-----------+-----------+
| user | table | column | row | old_value | new_value |
+--------+---------+--------+-----+-----------+-----------+
| admin | members | name | 123 | Joe | Bob |
+--------+---------+--------+-----+-----------+-----------+
| admin | members | age | 123 | 32 | 40 |
+--------+---------+--------+-----+-----------+-----------+
Obviously to populate this table you can see why I need to extract the values. Ideally I'd like to do it from existing MySQL query strings, passed into my PHP function (to handle existing code), but if I need to implement new PHP functions I'm open to suggestions.
Regex seems both complicated and painful since I'd have to format it for each column name for every table. Is there any parsing solution for this problem out there?

mysql insert into two tables using auto_increment value from the first table as a foreign key in the second

I want to insert into the cart table
**orderId** | cartId | cartDate | cartStatus
____________________________________________
1 | 1 | 20120102 | complete
2 | 2 | 20120102 | complete
3 | 3 | 20120102 | complete
4 | 4 | 20120102 | complete
using the auto increment value orderId from the order table
**orderId** | orderStatus | secret | sauce
____________________________________________
1 | 7 | 020200202 | bbq
2 | 6 | 020200202 | bbq
3 | 6 | 020200202 | t
4 | 4 | 020200202 | m
INSERT INTO ordertable VALUES(null,7,020200202,bbq)
but then using the orderId (which will now be 5)
INSERT INTO carttable VALUES(orderId,20120102,complete)
However,
this insert must be done as the same query. If I use mysql_last_id (php) there is an opportunity for someone else to insert into the database before my cart insert is executed. Or the connection might timeout. The database is MyISAM (and I can not change this, 3rd party solution).
Thank you,
J
I think your concern about using mysql_last_id is unfounded - it will return the last id for the current connection, not the last id globally across all connections.
So unless you have multiple threads sharing the same database connection or you perform another identity insert on the same connection before calling mysql_last_id, you should have nothing to worry about.
ETA: You could do this by sending multiple queries at once, like this:
INSERT INTO ordertable VALUES(null,7,020200202,bbq);
INSERT INTO carttable VALUES(LAST_INSERT_ID(),20120102,complete);
But if you are using mysql_query it usually won't let you send multiple queries in the same call (mostly as a security measure to try to prevent SQL injection).

Categories