How to calculate results in multiple tables with Eloquent ORM - mysql

I'm start learning MySQL and have some problem with selecting and calculating im multiple tables.
I have 2 tables:
First table "places"
id | name |
1 | restaurant |
Second table "ratings"
id | expert | place_id | design | personal | cooking
1 | expert 1 | 1 | 5 | 5 | 4
2 | expert 2 | 1 | 3 | 3 | 3
3 | expert 3 | 1 | 4 | 2 | 3
I select places with
$places = Place::all();
and used it
return view('place',compact('places'));
I need to use data from "rating" table with "places" and don't know how to do that
I need to find average of all values and average of each type of value AND use it with places.
How can I do this?

Asuming you're using atleast Laravel 5 you can make a has many relationship in your Place model.
Here is an example taken from the docs:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Place extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany('App\Rating');
}
}

Related

Eloquent Model: Define on-to-one relationship using flag on child table

I have 2 tables vendors and partners
vendors table only stores the name of some company and partners table store users who work for some company. So the structure is somethign like this:
vendors
| id | name |
+------+---------------+
| 1 | Vendor-1 |
| 2 | Vendor-2 |
| 3 | Vendor-3 |
Partners
| id | user_name | password |vendor_id | is_owner | is_admin | is_technitian |
+----+------------+-----------+----------+------------+------------+---------------+
| 1 | abc | ^&ASKJHA | 1 | 1 | 1 | 0 |
| 2 | def | ^&ASKJHA | 2 | 1 | 1 | 0 |
| 3 | ghi | ^&ASKJHA | 1 | 0 | 1 | 0 |
| 4 | jkl | ^&ASKJHA | 3 | 1 | 1 | 0 |
| 5 | mno | ^&ASKJHA | 1 | 0 | 0 | 1 |
| 6 | pqr | ^&ASKJHA | 2 | 0 | 1 | 0 |
| 7 | stu | ^&ASKJHA | 1 | 0 | 0 | 1 |
| 8 | vwx | ^&ASKJHA | 2 | 0 | 0 | 1 |
| 9 | yz | ^&ASKJHA | 3 | 0 | 0 | 1 |
So as you can see above that One partner is the owner of any vendor and rest of them work as employees for the vendor.
I am working with Eloquent ORM and i have already defined Models for both Partner and vendor. I want to add an owner method in the Vendor Model so i can directly access the owner for any vendor Object. What i want to know is how do i relate this in my model defination. Is it do-able or do i need to make some changes in my database structure ?
class Vendor extends Model{
/**
* Get all the users for this vendor
*/
public function users(){
$this->hasMany(Partner::class);
}
public function owner(){
// how do i relate one owner from partner model who has is_owner == 1
}
}
Try with where()
public function owner(){
$this->hasOne(Partner::class)->where('is_owner', true);
}
You may have to specify the foreign key in the relationship.
Instead of defining a new relationship, and leading to duplicate code and breaking the DRY principle, make use of Local Scopes which comes out of the box in Eloquent ORM.
Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, simply prefix an Eloquent model method with scope.
Scopes should always return a query builder instance:
for instance:- define a function ScopeOwner() in your Vendor model such as:
public function scopeOwner($query)
{
return $query->where('is_owner', 1);
}
and then use it in your controller or implement it as follows:
$vendor->users()->owner()->get();
Once the scope has been defined, you may call the scope methods when querying the model. However, you do not need to include the scope prefix when calling the method. You can even chain calls to various scopes.
What's next, you could even use dynamic scopes to get even better code re-usage.
Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope. Scope parameters should be defined after the $query parameter:
public function scopeOfType($query, $type)
{
return $query->where('is_owner', $type);
}
and consume it as follows:
$vendor->users()->ofType(0)->get();
for more information, check back to official docs: Eloquent ORM Scopes

MySQL - Use table with previous states to show status at that time

I have a MySQL database that has a member table where there are multiple membership levels (e.g., Basic, Premium) of which a member can have one, and the membership level can change over time. I am working on constructing a query to give a count of the members of each type at any time, so the membership type of each member may be different than it is currently. To do this, I have a few additional tables.
types - Gives id associated with each membership type and its string
timeunits - Gives id associated with each time unit and its string
oldtypes - Gives the old membership type for a member and the time at which it was changed from that type
I know if I want to get a count of the members of each type currently, I can use:
SELECT types.type_desc, COUNT(*) FROM member INNER JOIN types ON member.type = types.type_id GROUP BY types.type_id;
What I'm not sure about is how to get the appropriate values from the oldtypes table given a time unit and use those with the member table to get a snapshot of the members and their memberships at any time. So if I were to get the memberships at timeunit = 2, I would see as a result:
| member_id | name | type |
|-----------|---------|------|
| 1 | Alice | 1 |
| 2 | Bob | 2 |
| 3 | Charlie | 1 |
| 4 | Daisy | 2 |
| 5 | Evan | 1 |
| 6 | Frank | 1 |
| 7 | Gloria | 2 |
| 8 | Henry | 2 |
SQL Fiddle
EDIT: The oldtypes table is constructed such that it gives the membership a member had prior to the listed time, at which point it was changed. So a row reading 4,1,2 indicates that member 4 changed from membership 1 at time 2.

how can we add two field in same column in sql table?

i want to create a database that consists all products rating and notes purchase by user
suppose that i create a table name "Users_rating_product" that consists some column
user_id ->INT
company->Varchar
product1 -> 3 (rating of product out of 5)|(review of product)
product2-> 4(rating)|(some review)
i want to know how can i do it in mysql . i'm using phpmyadmin for database creation
table looks like
user_id | company |Ac | TV | fridge
1 | goderaj |3 ,take more power |4,less power |5,efficient
i want to know how can i do this so that both rating and notes for product display in same column
For this you'll need a separate table. Call it ProductRating. Put a unique key (ID) to the table and reference that key to your Users_rating_product table.
For example:
Users_rating_product:
user_id | company |Ac | TV | fridge
1 | goderaj |1 | 2 | 3
2 | somecomp |4 | 5 | 6
ProductRating
ID | Rating | Review
1 | 3 | Take more power
2 | 4 | less power
3 | 5 | efficient
4 | 5 | excellent
5 | 1 | awful
6 | 3 | average
There are many ways that you can achieve your functionality.
but this is not standard format to write queries.
you can have comma seperated column in your database like this
user_id | company |Ac_mapping_id
1 | goderaj |1,REview for Ac
for Seperate two values you can do like this
PARSENAME(REPLACE(String,',','.'),2) 'Name'
for Detail Reference for splitting two values you can refer :
click To refer
Better Way is to store Product description in different table.
You can easily write queries to retrieve your data
user_id | company |Ac_mapping_id | TV_mapping_id | fridge_mapping_id
1 | goderaj |1 | 2 | 3
& store all the ratings & reviews in mapping table like this
Ac_mapping_id | Rating |Review
1 | 1 |abcd
So on for the other table.
For Retrieving all the data just Use Left outer join
Select
*
from
Users_rating_product mm
Left outer join Ac_mapping_table ac on ac.ac_mapping_id = mm.ac_mapping_id
.....so on

Complex conditions on Eloquent Joins

I'm looking to use Eloquent to output some data from our database, but the database can vary.
Below is an illustration of my database tables
Webservice Tags:
----------------------------------------------------------
| id | webservice_tag | webservice_name | blog_id |
==========================================================
| 1 | TEST, TESSST, TES | Test Service | 1 |
----------------------------------------------------------
| 2 | OPTION, OPT, EXAMPLE | Example Service | 1 |
----------------------------------------------------------
| 3 | ANOTHER, ANO, THER | Another Service | 1 |
----------------------------------------------------------
Blog Post:
----------------------------------------------------------
| id | title | blog_id | tag |
==========================================================
| 1 | Blog Title 1 | 1 | THER |
----------------------------------------------------------
| 2 | Blog Title 2 | 1 | TES |
----------------------------------------------------------
| 3 | Blog Title 3 | 1 | ANOTHER |
----------------------------------------------------------
So here, we have two tables. Blog Posts and Web-service Tags.
Our blog posts are populated by a number of web-services from a number of different providers. Test Service, Example Service and Another Service for example. However, these web-services are very inconsistent; they'll send over a combination of tags, and no two posts are guaranteed to be the same.
So, we've created a table called Webservice Tags which is designed to log each of these occurrances. This way we can identify that (in the examples) Blog Title 3 was sent by Another Service, Blog Title 2 was sent by Test Service etc.
I'm developing reports to show how many posts we get from each of our web services. So for each blog post, I need to identify the web service and get the web service name that's associated with it. We have multiple blogs, each with their own web services (some may share a tag), so this report needs to be isolated to each individual blog.
Here's the query in Eloquent:
$query = DB::table('blog_posts')
->join('webservice_tags', function($join) use ($blog) {
$join->on('blog_posts.tag', '=', 'webservice_tags.webservice_tag')
->where('webservice_tags.blog_id', '=', $blog->id);
})
->addSelect('webservice_tags.webservice_name AS name')
->addSelect(DB::raw("COUNT(blog_posts.id) AS count"))
->where('blog_posts.blog_id', '=', $blog->id)
->groupBy('webservice_tags.webservice_name')
->get();
This query was fine whilst the Webservice sent a consistent tag. Now however there are different tags per web service and this report needs to count them all equally.
Here's my amendment, but it's not working as expected:
->join('webservice_tags', function($join) use ($blog) {
$join->on('blog_posts.tag', 'LIKE', DB::raw('CONCAT("%", webservice_tags.webservice_tag, "%")'))
->where('webservice_tags.blog_id', '=', $blog->id);
})
I'm not getting any matches in the query through this method.
The query aims to join any field to the SELECT where the blog_posts.tag is within the comma separated list of webservice_tags.
Is there a way to do this more effectively?
Explanation on the blog_id relationship
This system manages multiple blogs, as well as the posts within those blogs. A webservice may be applicable to one blog, but not the other, which is why the blog_id is associated witih webservices as well as the individual posts. There are things like commission percentages that are associated with a webservice, and Test Webservice may offer 10% for Blog A, and 12% for Blog B, so they are essentially separate.
Quickest/crude solution:
The reason you do not see any result is because of this:
$join->on('blog_posts.tag', 'LIKE', DB::raw('CONCAT("%", webservice_tags.webservice_tag, "%")'))
which translates to:
INNER JOIN `webservice_tags`
ON `blog_posts`.`tag` LIKE CONCAT("%", webservice_tags.webservice_tag, "%")
This tries to match blog_posts.tag that has values like %ANOTHER, ANO, THER%.
So let's say blog_posts.tag is "ANOTHER" and webservice_tags.webservice_tag is "ANOTHER, ANO, THER". Rather than matching "ANOTHER", the database is trying to match values like these:
ANOTHER != FOOANOTHER, ANO, THER
ANOTHER != ANOTHER, ANO, THERBAR
ANOTHER != FOOANOTHER, ANO, THERBAR
Which is the reason why you are not getting any results. You will need to swap the columns around, like this:
$join->on('webservice_tags.webservice_tag', 'LIKE', DB::raw('CONCAT("%", blog_posts.tag, "%")'))
Longer solution:
I'm just going to throw my idea here. These are some points that come into my mind when thinking about this:
Tag <-> Web Service mapping is stored as a string when it is actually a set of data. That makes it hard to search and hard to perform queries on it. I like to break them down into two or more tables. This is called Database normalization. Don't ask me which level it is, I never understood the technical description for them. :D
Since you mentioned that a web service can have many tags, but some tags can also be shared by different web services, we will need a pivot table to handle this many-to-many relationship.
When aggregating, I like to start from top down. So I write queries starting from webservices, then work my way down to blog posts. This is the opposite of your example.
I'm ignoring blog_id columns in webservices table for now since I don't quite understand yet what they do.
So first I would actually try to normalize the tables first into webservices, webservice_tags, tags, and blog_posts, like this:
webservices:
-------------------------
| id | webservice_name |
=========================
| 1 | Test Service |
-------------------------
| 2 | Example Service |
-------------------------
| 3 | Another Service |
-------------------------
webservice_tags:
------------------------------------------
| id | tag_name | webservice_id |
==========================================
| 1 | TEST | 1 |
------------------------------------------
| 2 | TESSST | 1 |
------------------------------------------
| 3 | TES | 1 |
------------------------------------------
| 4 | OPTION | 2 |
------------------------------------------
| 5 | OPT | 2 |
------------------------------------------
| 6 | EXAMPLE | 2 |
------------------------------------------
| 7 | ANOTHER | 3 |
------------------------------------------
| 8 | ANO | 3 |
------------------------------------------
| 9 | THER | 3 |
------------------------------------------
| 10 | FOO | NULL |
------------------------------------------
| 11 | BAR | NULL |
------------------------------------------
Note that I'm using tag_name in the pivot table above for the sake of readability. I guess it would be better to use tag_id instead.
tags:
--------------------------
| id | tag_name |
==========================
| 1 | TEST |
--------------------------
| 2 | TESSST |
--------------------------
| 3 | TES |
--------------------------
| 4 | OPTION |
--------------------------
| 5 | OPT |
--------------------------
| 6 | EXAMPLE |
--------------------------
| 7 | ANOTHER |
--------------------------
| 8 | ANO |
--------------------------
| 9 | THER |
--------------------------
| 10 | FOO |
--------------------------
| 11 | BAR |
--------------------------
blog_posts:
-----------------------------------------------------------
| id | title | blog_id | tag_name |
===========================================================
| 1 | Blog Title 1 | 1 | THER |
-----------------------------------------------------------
| 2 | Blog Title 2 | 1 | TES |
-----------------------------------------------------------
| 3 | Blog Title 3 | 1 | ANOTHER |
-----------------------------------------------------------
Now to get a report of how many blog posts are created by each web service, we can do this with a join query.
In this case a QueryBuilder query would give a better performance since we want to know just the aggregates, not the actual database models:
$report = DB::table('webservices')
->leftJoin('webservice_tags', 'webservice_tags.webservice_id', '=', 'webservices.id')
->leftJoin('tags', 'tags.tag_name', '=', 'webservices_tags.tag_name')
->leftJoin('blog_posts', 'blog_posts.tag_name', '=', 'tags.tag_name')
->where('blog_posts.blog_id', '=', $blog->id)
->select(['webservices.webservice_name', DB::raw('COUNT(`blog_posts.id`) as `num_posts`')])
->groupBy('webservices.id')
->get();
You would now get a report of how many blog posts were created by each web service, for all web services.
One note is that, since you mentioned a web service may share tags, and you are identifying web service <-> blog posts via tags,
you cannot 100% accurately determine the origin web service because you won't know for sure which web service set that tag for that post.
Extra: You might notice that I also added FOO and BAR to the Webservice Tags.
This would help in case, like you said that web services are "very inconsistent", new tags would be added to your database.
You could also query them and generate a report easily to find out which tag hasn't been mapped yet.

How to store multiple values in single column where use less memory?

I have a table of users where 1 column stores user's "roles".
We can assign multiple roles to particular user.
Then I want to store role IDs in the "roles" column.
But how can I store multiple values into a single column to save memory in a way that is easy to use? For example, storing using a comma-delimited field is not easy and uses memory.
Any ideas?
If a user can have multiple roles, it is probably better to have a user_role table that stores this information. It is normalised, and will be much easier to query.
A table like:
user_id | role
--------+-----------------
1 | Admin
2 | User
2 | Admin
3 | User
3 | Author
Will allow you to query for all users with a particular role, such as SELECT user_id, user.name FROM user_role JOIN user WHERE role='Admin' rather than having to use string parsing to get details out of a column.
Amongst other things this will be faster, as you can index the columns properly and will take marginally more space than any solution that puts multiple values into a single column - which is antithetical to what relational databases are designed for.
The reason this shouldn't be stored is that it is inefficient, for the reason DCoder states on the comment to this answer. To check if a user has a role, every row of the user table will need to be scanned, and then the "roles" column will have to be scanned using string matching - regardless of how this action is exposed, the RMDBS will need to perform string operations to parse the content. These are very expensive operations, and not at all good database design.
If you need to have a single column, I would strongly suggest that you no longer have a technical problem, but a people management one. Adding additional tables to an existing database that is under development, should not be difficult. If this isn't something you are authorised to do, explain to why the extra table is needed to the right person - because munging multiple values into a single column is a bad, bad idea.
You can also use bitwise logic with MySQL. role_id must be in BASE 2 (0, 1, 2, 4, 8, 16, 32...)
role_id | label
--------+-----------------
1 | Admin
2 | User
4 | Author
user_id | name | role
--------+-----------------
1 | John | 1
2 | Steve | 3
3 | Jack | 6
Bitwise logic allows you to select all user roles
SELECT * FROM users WHERE role & 1
-- returns all Admin users
SELECT * FROM users WHERE role & 5
-- returns all users who are admin or Author because 5 = 1 + 4
SELECT * FROM users WHERE role & 6
-- returns all users who are User or Author because 6 = 2 + 4
From your question what I got,
Suppose, you have to table. one is "meal" table and another one is "combo_meal" table. Now I think you want to store multiple meal_id inside one combo_meal_id without separating coma[,]. And you said that it'll make your DB to more standard.
If I not getting wrong from your question then please read carefully my suggestion bellow. It may be help you.
First think is your concept is right. Definitely it'll give you more standard DB.
For this you have to create one more table [ example table: combo_meal_relation ] for referencing those two table data. May be one visible example will clear it.
meal table
+------+--------+-----------+---------+
| id | name | serving | price |
+------+--------+-----------+---------+
| 1 | soup1 | 2 person | 12.50 |
+------+--------+-----------+---------+
| 2 | soup2 | 2 person | 15.50 |
+------+--------+-----------+---------+
| 3 | soup3 | 2 person | 23.00 |
+------+--------+-----------+---------+
| 4 | drink1 | 2 person | 4.50 |
+------+--------+-----------+---------+
| 5 | drink2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 6 | drink3 | 2 person | 5.50 |
+------+--------+-----------+---------+
| 7 | frui1 | 2 person | 3.00 |
+------+--------+-----------+---------+
| 8 | fruit2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 9 | fruit3 | 2 person | 4.50 |
+------+--------+-----------+---------+
combo_meal table
+------+--------------+-----------+
| id | combo_name | serving |
+------+--------------+-----------+
| 1 | combo1 | 2 person |
+------+--------------+-----------+
| 2 | combo2 | 2 person |
+------+--------------+-----------+
| 4 | combo3 | 2 person |
+------+--------------+-----------+
combo_meal_relation
+------+--------------+-----------+
| id | combo_meal_id| meal_id |
+------+--------------+-----------+
| 1 | 1 | 1 |
+------+--------------+-----------+
| 2 | 1 | 2 |
+------+--------------+-----------+
| 3 | 1 | 3 |
+------+--------------+-----------+
| 4 | 2 | 4 |
+------+--------------+-----------+
| 5 | 2 | 2 |
+------+--------------+-----------+
| 6 | 2 | 7 |
+------+--------------+-----------+
When you search inside table then it'll generate faster result.
search query:
SELECT m.*
FROM combo_meal cm
JOIN meal m
ON m.id = cm.meal_id
WHERE cm.combo_id = 1
Hopefully you understand :)
You could do something like this
INSERT INTO table (id, roles) VALUES ('', '2,3,4');
Then to find it use FIND_IN_SET
As you might already know, storing multiple values in a cell goes against 1NF form. If youre fine with that, using a json column type is a great way and has good methods to query properly.
SELECT * FROM table_name
WHERE JSON_CONTAINS(column_name, '"value 2"', '$')
Will return any entry with json data like
[
"value",
"value 2",
"value 3"
]
Youre using json, so remember, youre query performance will go down the drain.