I have 4 tables:
MusicGenre
Artist
Song
SongInfo
weekly_hit is a column on the SongInfo table.
MusicGenre is connected to Artist like this:
public function artists()
{
return $this->hasMany('App\Models\Artist\ArtistInfo','genre_id','id');
}
Artist is connected to Song like this:
public function songs()
{
return $this->hasMany('App\Models\Song\Songs','artist_id');
}
And Song is connected to SongInfo like this:
public function info()
{
return $this->hasOne('App\Models\Song\SongsInfo','song_id','id');
}
There is a no problem querying the table.
My problem is that I want to get the best music genres using weekly_hit in SongInfo table.
Edit:
I resolved that problem with raw code
"select music_genres.*,
sum(distinct song_info.weekly_hit) as song_popularity
from `music_genres`
left join `artist_info` on
`music_genres`.`id` = `artist_info`.`genre_id`
left join songs on
artist_info.artist_id = songs.artist_id
left join songs_info on
songs.id = songs_info.song_id
group by music_genres.name
order by song_popularity DESC
limit 5
But, I can't get songs. I want to get 5 song ordered by weekly_hit in songs_info table from all returned music genres.
Guys i still searching a solution?
Can somebody help me?
You'll want to use Eloquent's hasManyThrough() relation to reach that deep in the relationship chain.
Related
(Just started learning SQL a few days ago so sorry if this is a stupid question!)
I have three tables, Users, Addresses, and AddressCategories. Each User has multiple Addresses, but no more than 1 Address per AddressCategory. I would like to make a single query that searches for Users based on different criteria for each AddressCategory.
Table structure looks like:
Users:
id
1
2
AddressCategories:
category
HomeAddress
WorkAddress
Addresses:
userId category address
1 HomeAddress 1 Washington Street
1 WorkAddress 53 Elm Avenue
2 HomeAddress 7 Bernard Street
Let's say I want to search for all users whose home address contains the word "Street" and work address contains the word "Avenue". I can use the query:
SELECT * FROM Users
INNER JOIN Addresses a1 ON Users.id=a1.userId
INNER JOIN Addresses a2 ON Users.id=a2.userId
WHERE a1.category='HomeAddress' AND a1.address LIKE '%Street%'
AND a2.category='WorkAddress' AND a2.address LIKE '%Avenue%'
If I want to query across an arbitrary number of AddressCategories, I can dynamically build a query using the same principle above:
// dictionary of query parts
var q_parts = {HomeAddress: 'Street',
WorkAddress: 'Avenue'
...}
// build the query string piece by piece
let q_str1="", q_str2="";
let i=0;
for (q in q_parts) {
i++;
q_str1 += "INNER JOIN Addresses a${i} ON Users.id=a${1}.userId ";
q_str2 += (i==1) ? "WHERE " : "AND ";
q_str2 += "a${i}.category='${q}' AND a${i}.address LIKE '%${q_parts[q]}%' ";
}
// complete query string
let q_str = "SELECT * FROM Users "+q_str1+q_str2;
The way I'm doing it now works, but it's easy to make a mistake building the query string and the final string quickly becomes enormous as the number of categories grows. Seems like there must be a better way. What is the right way to perform such queries in MySQL? (Or is there a problem with how I've organized my tables?)
You can use this one for query building.
Official site: https://knexjs.org/
Npm link: https://www.npmjs.com/package/knex
A sample SQL for don't have to join many times. It is not tested, and just an idea.
You can use When/then in Where clause for verifying case by case. And finally, filter base on the total categories of a User (group by).
SELECT *
FROM
Users Inner Join
(SELECT userId,
count(category) AS categoryCount
WHERE address LIKE '%Street%' LIKE CASE
WHEN category = 'HomeAddress' THEN '%Street%'
WHEN category = 'WorkAddress' THEN '%Avenue%'
END
GROUP BY userId) a ON Users.id = a.userId
WHERE categoryCount = ? -- inject your count of all categories here, maybe get from another query
The is the table structure,which basically has three tables,namely expenses,categories and sunbcategories
table expenses
(id,category_id,sub_category_id,date,description,amount)
table categories
(id,category_name)
table subcategories
(id,sub_category_name,category_id)
This is the SQL query that is needed
select expense.date, expense.description, expense.amount,
category.category_name, subcategory.sub_category_name
from expenses as expense,categories as category,subcategories as subcategory
where expense.category_id=category.id and
category.id=subcategory.category_id);
This is the function in Expense model with which I pass the category_id
The same query mentioned above is written in laravel, but I am not able to
fetch the data.
function fetchExpenseData($categoryId)
{
$expense = Expense::select("expenses.*","categories.category_name as
Categoryname","subcategories.Sub_category_name")
->join("categories","categories.id","=","expenses.category_id");
->join("subcategories",function($join)
{
$join>on("subcategories.category_id","=","expenses.category_id")
->on("suncategories.id","=","expenses.sub_category_id")
})->get();
return $expenses;
}
$expenses that are returned will be printed in blade.php.
Can I know what is the mistake
thanks in advance
Hye there ,
You need to add eloquent model for retrieving data fromenter code here three tables
Like
I have School Table , Student Table , Teacher Table
School is relating with both Student and Teacher then we will add relationship
In School Model
`
public function getStudent(){
return $this->hasMany('student_id' , App\Student);
}
public function getTeachers(){
return $this->hasMany('teacher_id' , App\Teacher);
}
In Student table
public function getSchool(){
return $this->hasOne('school_id' , App\School);
}
`
now call data from student
`
$students = Student::with('getSchool.getTeachers')->get()
This Demonstration for what I have get from your Question
I have defined three models that are as below
class City(models.Model):
city_name = models.CharField(...)
class Restaurant(models.Model):
restaurant_name = models.CharField(...)
city = models.ManyToManyFields(City)
class MenuItemCount(models.Model):
restaurant = models.ForeignKey(Restaurant)
city = models.ForeignKey(City)
menu_count = models.IntegerField()
One note is that all restaurants and cities might not exist in MenuCount. So a left join is needed.
I am trying to write the django query to retrieve a list of restaurant name, city name and menu counts. Example result would be
restaurant1, city1, 20
restaurant1, city2, None
restaurant2, city2, 30
restaurant3, city1, None
How can I write a query for this?
If you want to get all of the data in 1 query, you can use prefetch_related. You can see the documentation here. Here is the example.
City.objects.prefetch_related('restaurant_set', 'menuitemcount_set').all()
This query gives a pagination of all 'albums' with a picture and description for each. Now I am trying to get always the latest picture of each album.
I have tried to add a second orderBy('pics.created_at') , but that did not work. I think I need some kind of subquery but don't know how.
$query = AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
pics.created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
->join('albums','albums.id','=','album_pic.album_id')
->join('pics','pics.id','=','album_pic.pic_id')
->join('users','users.id','=','pics.user_id');
if(!is_null($user_id))
$query->where('album_pic.user_id',$user_id);
$albums = $query->groupBy('albums.id')
->orderBy('albums.created_at','desc')
->paginate(20);
edit
I made a mistake. I don't have created_at and updated_at in the album_pic table .
So my 'Album' - model/relations are now like this:
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('pics.created_at');
}
And the query now looks like this:
$q = Album::with('pics')->with('latestPic.users');
if(!is_null($user_id))
$q->where('albums.user_id',$user_id);
$albums = $q->orderBy('albums.created_at','desc')
->paginate(20);
This works. Only thing I would like to improve is the way, the pictures per album are counted. Now I get all with with('pics') and then do a count($album->pics) in the view. If there is a way to not load everything, but only count the pictures, it would be nice.
You need to get the MAX(created_at) inside a subquery; see MySQL select MAX(datetime) not returning max value for example.
Really, though, if you're doing this in Laravel, it would be better to set these all up as relations and leverage the power of Eloquent. Then, you can define a relationship for pictures that uses ->latest() to return the most recent. See laravel eloquent query group by last id for an example (which uses one table, but the principle is the same for multiple tables).
Here's how you could set this up using Eloquent relations:
User model (User.php)
class User extends Eloquent {
public function albums()
{
return $this->hasMany('Album');
}
}
Album model (Album.php)
class Album extends Eloquent {
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('album_pic.created_at');
}
}
Because you have a many-to-many relationship between albums and pics, in the latestPic() relation, you must specify the album_pic.created_at field for latest()—since we are actually interested in the order of entries in the pivot table, rather than in the pics table.
Finally, link this all together. For example, for a user with id of 1:
$albums = User::find(1)->albums()->with('pics')->with('latestPic')->paginate(20);
foreach($albums as $album) {
echo('<br>Album:');
var_dump($album->title);
echo('All pics:');
foreach($album->pics as $pic) {
var_dump($pic->url);
}
echo('Latest pic:');
$latestPic = $album->latestPic->first();
if ($latestPic) {
var_dump($latestPic->url);
}
}
Note that we are eager loading the pics and latestPic to reduce the number on calls to the database. Also note that accessing the $latestPic->url is wrapped in an if statement, otherwise albums that do not have any photos will throw an error since $album->latestPic would return null.
As #cedie correctly noted, Laravel doesn't handle pagination all that efficiently when using a groupBy statement, but that shouldn't be a problem in this case. The underlying queries do not use groupBy, so you should be save to use ->paginate(20).
Try using this in your select query:
max(pics.created_at) as created_at
instead of this:
pics.created_at
So your code should look like this:
AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
max(pics.created_at) as created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
Perhaps ypu can figure out how to adapt this for your purposes...
SELECT ap.*
, p.*
FROM album_pic ap
JOIN pics p
ON p.id = ap.pic_id
JOIN
( SELECT ap.*
, MAX(p.created_at) max_created_at
FROM album_pics ap
JOIN p.*
ON p.id = ap.pic_id
) x
ON x.album_id = ap.album_id
AND x.max_created_at = p.created_at;
I have two tables. One for artists (artist_id, artist_name), one for tracks (track_id, artist_id, track_title).
I want to be able to handle queries like "better man pearl jam" but I'm having trouble thinking about the logic on how this would work. better man is the track title while pearl jam is the artist name.
We're currently using sphinx to handle our search engine (artists and tracks individually).
is there an efficient way to go about this?
I would think that if someone does a search that matches both track and artist, they're looking for the track. What is your desired behaviour in this case?
Based on your comment, your sphinx query should look like this then:
SELECT track_id, track_title, artists.artist_id, artist_name FROM tracks
LEFT JOIN artists WHERE artists.artist_id = tracks.artist_id;
If you're using PHP, your code would look something like:
$res = $sc->Query($searchString, "SphinxIndexName");
if (isset($res['matches']) && sizeof($res['matches']) > 0) {
ids = join(",", array_keys($res["matches"]));
$query = "SELECT track_id, track_title, artists.artist_id, artist_name FROM tracks
LEFT JOIN artists WHERE artists.artist_id = tracks.artist_id ORDER BY FIELD(track_id, $ids)";
// ... query MySQL and display results
}
else {
echo "no results";
}