I am trying to fetch the second highest date from a relation-table in with Eloquent Laravel 6.
MoodTable
id animal_id date mood
1 1 2019-12-14 happy
2 1 2019-12-11 drunk
3 1 2019-12-13 sad
AnimalTable
id name
1 Dog
So for example, I want to be able to query: "Today Dog is happy. Prevously to that he was Drunk.
To fetch the highest I use:
return $this->hasMany('App\AnimalMood', 'animal_id')->select(DB::raw('
mood,
animal_id,
MAX(date) as max_date,
'))
->groupBy('stock_id');
However, when it comes to seconed highest date... I'm out...
I have looked at How to return second newest record in SQL?
for some answers,but not been able to put that in a relationship senario nor translate it to Eloquent.
Ideally, I would like to run Animal::with('moodPrevious')->get() and Animal::find(1)->moodPrevious from my controller...
I would change the relationship and grab all of the related models to your Animal model when you query for it.
return $this->hasMany('App\AnimalMood', 'animal_id');
Below returns the animals with the moods pre-sorted.
$animals = Animal::with(['mood' => function($q){
$q->orderByDesc('date');
}])->get();
Laravel offers a lot of collection methods that work well with related models. I would use the callback you can provide to first() in order to get the mood you want on the model. You probably already have the specific model you want, either by foreaching over the above collection or something similar. The relation will be a collection instance so we use the collection method to get the mood desired.
$previousMood = $animal->mood->first(function($value, $key){
return $key == 1 // You can use whatever here, this will return the second item on the relation, or the previous mood.
});
See https://laravel.com/docs/5.8/collections#method-first for a reference.
I think this would work
return $this->hasMany('App\AnimalMood', 'animal_id')
->select(
DB::raw('
mood,
animal_id,
MAX(date) as max_date,
')
)
->where(
DB::raw("
date = (
SELECT
MAX(date)
FROM animal_moods
WHERE date < (
SELECT
MAX(date)
FROM
animal_moods
)
)
")
)
->groupBy('stock_id');
Related
im new in Laravel and i want to count repetitive registers in a field name "career" from a table named "students"
Table students
career
-------
1
1
2
1
desired output
---------------
3
1
indicating
"three" repetitive ones (1) and
"one" number (2)
i am using $results = DB::select('select * from students', array(1)); to read career field with the output 1121
how can i do in order to count repetitive registers ? anyhelp is appreciated
you need to count all records by grouping the career field, this is more related to mysql here is the fiddle for mysql
http://sqlfiddle.com/#!9/b03713/4/0
it'll give you following records
for Laravel you can run the following query,
$career = DB::table('students')
->select('career', DB::raw('count(*) as registers'))
->groupBy('career')
->get();
Here is the raw MySQL query you want:
SELECT COUNT(*) AS cnt
FROM students
GROUP BY career
ORDER BY career;
It should be straightforward to figure out how to port the above to Laravel code.
$results = DB::table("students")
->select("count (*) as cnt")
->where(DB::raw("career"))
->groupBy("career")
->get();
Goodmorning everyone.
I'm going crazy
I need to extract the number of invoices from the current year per customer.
in my table tbl_preventivi I have the field anagrafica_id for the customer and date_prev for the date of the invoice.
this is my code.
$anno = date('Y');
SELECT tbl_preventivi.anagrafica_id, Count(tbl_preventivi.preventivo_id) AS totale
FROM tbl_preventivi
GROUP BY tbl_preventivi.anagrafica_id, Year(tbl_preventivi.data_prev)
HAVING ((Year(tbl_preventivi.data_prev) = ".$anno.") AND (tbl_preventivi.anagrafica_id=".$_GET['anagrafica_id']."))
i am sure that in the test i am doing the result must be 1, instead the query is null.
if I remove
(Year(tbl_preventivi.data_prev) = ".$anno.") AND the query works and returns 6 (which is the number of invoices made even in previous years).
where am i wrong?
The simplest solution would be something like:
SELECT p.`anagrafica_id`,
COUNT(p.`preventivo_id`) as `totale`
FROM `tbl_preventivi` p
WHERE p.`anagrafica_id` = 12345 and YEAR(p.`data_prev`) = 9999
GROUP BY p.`anagrafica_id`;
Note: 12345 should be replaced with a sanitized customer number, and 9999 should be replaced with a sanitized and logical year. Prepared statements are your friend here.
If you wish to see all invoices for a customer across all years, you can do this:
SELECT p.`anagrafica_id`,
YEAR(p.`data_prev`) as `year`,
COUNT(p.`preventivo_id`) as `totale`
FROM `tbl_preventivi` p
WHERE p.`anagrafica_id` = 12345
GROUP BY p.`anagrafica_id`, `year`
ORDER BY `year` DESC;
Again, be sure to sanitize your inputs. In the famous words of Fox Mulder: Trust no one.
I'm getting grey hair by now...
I have a table like this.
ID - Place - Person
1 - London - Anna
2 - Stockholm - Johan
3 - Gothenburg - Anna
4 - London - Nils
And I want to get the result where all the different persons are included, but I want to choose which Place to order by.
For example. I want to get a list where they are ordered by LONDON and the rest will follow, but distinct on PERSON.
Output like this:
ID - Place - Person
1 - London - Anna
4 - London - Nils
2 - Stockholm - Johan
Tried this:
SELECT ID, Person
FROM users
ORDER BY FIELD(Place,'London'), Person ASC "
But it gives me:
ID - Place - Person
1 - London - Anna
4 - London - Nils
3 - Gothenburg - Anna
2 - Stockholm - Johan
And I really dont want Anna, or any person, to be in the result more then once.
This is one way to get the specified output, but this uses MySQL specific behavior which is not guaranteed:
SELECT q.ID
, q.Place
, q.Person
FROM ( SELECT IF(p.Person<=>#prev_person,0,1) AS r
, #prev_person := p.Person AS person
, p.Place
, p.ID
FROM users p
CROSS
JOIN (SELECT #prev_person := NULL) i
ORDER BY p.Person, !(p.Place<=>'London'), p.ID
) q
WHERE q.r = 1
ORDER BY !(q.Place<=>'London'), q.Person
This query uses an inline view to return all the rows in a particular order, by Person, so that all of the 'Anna' rows are together, followed by all the 'Johan' rows, etc. The set of rows for each person is ordered by, Place='London' first, then by ID.
The "trick" is to use a MySQL user variable to compare the values from the current row with values from the previous row. In this example, we're checking if the 'Person' on the current row is the same as the 'Person' on the previous row. Based on that check, we return a 1 if this is the "first" row we're processing for a a person, otherwise we return a 0.
The outermost query processes the rows from the inline view, and excludes all but the "first" row for each Person (the 0 or 1 we returned from the inline view.)
(This isn't the only way to get the resultset. But this is one way of emulating analytic functions which are available in other RDBMS.)
For comparison, in databases other than MySQL, we could use SQL something like this:
SELECT ROW_NUMBER() OVER (PARTITION BY t.Person ORDER BY
CASE WHEN t.Place='London' THEN 0 ELSE 1 END, t.ID) AS rn
, t.ID
, t.Place
, t.Person
FROM users t
WHERE rn=1
ORDER BY CASE WHEN t.Place='London' THEN 0 ELSE 1 END, t.Person
Followup
At the beginning of the answer, I referred to MySQL behavior that was not guaranteed. I was referring to the usage of MySQL User-Defined variables within a SQL statement.
Excerpts from MySQL 5.5 Reference Manual http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
"As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement."
"For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed."
"the order of evaluation for expressions involving user variables is undefined."
Try this:
SELECT ID, Place, Person
FROM users
GROUP BY Person
ORDER BY FIELD(Place,'London') DESC, Person ASC;
You want to use group by instead of distinct:
SELECT ID, Person
FROM users
GROUP BY ID, Person
ORDER BY MAX(FIELD(Place, 'London')), Person ASC;
The GROUP BY does the same thing as SELECT DISTINCT. But, you are allowed to mention other fields in clauses such as HAVING and ORDER BY.
I've got a MySQL database with lots of entris of highscores for a game. I would like to get the "personal best" entry with the max value of score.
I found a solution that I thought worked, until I got more names in my database, then it returnes completely different results.
My code so far:
SELECT name, score, date, version, mode, custom
FROM highscore
WHERE score =
(SELECT MAX(score)
FROM highscore
WHERE name = 'jonte' && gamename = 'game1')
For a lot of values, this actually returns the correct value as such:
JONTE 240 2014-04-28 02:52:33 1 0 2053
It worked fine with a few hundred entries, some with different names. But when I added new entries and swapped name to 'gabbes', for the new names I instead get a list of multiple entries. I don't see the logic here as the entries in the database seem quite identical with some differences in data.
JONTE 176 2014-04-28 11:03:46 1 0 63
GABBES 176 2014-04-28 11:09:12 1 0 3087
The above has two entires, but sometimes it may also return 10-20 entries in a row too.
Any help?
If you want the high score for each person (i.e. personal best) you can do this...
SELECT name, max(score)
FROM highscore
WHERE gamename = 'game1'
GROUP BY name
Alternatively, you can do this...
SELECT name, score, date, version, mode, custom
FROM highscore h1
WHERE score =
(SELECT MAX(score)
FROM highscore h2
WHERE name = h1.name && gamename = 'game1')
NOTE: In your SQL, your subclause is missing the name = h1.name predicate.
Note however, that this second option will give multiple rows for the same person if they recorded the same high score multiple times.
The multiple entries are returned because multiple entries have the same high score. You can add LIMIT 1 to get only a single entry. You can choose which entry to return with the ORDER BY clause.
I've currently got a table as follows,
Column Type
time datetime
ticket int(20)
agentid int(20)
ExitStatus varchar(50)
Queue varchar(50)
I want to write a query which will break this down by week, providing a column with a count for each ExitStatus. So far I have this,
SELECT ExitStatus,COUNT(ExitStatus) AS ExitStatusCount, DAY(time) AS TimePeriod
FROM `table`
GROUP BY TimePeriod, ExitStatus
Output:
ExitStatus ExitStatusCount TimePeriod
NoAgentID 1 4
Success 3 4
NoAgentID 1 5
Success 5 5
I want to change this so it returns results in this format:
week | COUNT(NoAgentID) | COUNT(Success) |
Ideally, I'd like the columns to be dynamic as other ExitStatus values may be possible.
This information will be formatted and presented to end user in a table on a page. Can this be done in SQL or should I reformat it in PHP?
There is no "general" solution to your problem (called cross tabulation) that can be achieved with a single query. There are four possible solutions:
Hardcode all possible ExitStatus'es in your query and keep it updated as you see the need for more and more of them. For example:
SELECT
Day(Time) AS TimePeriod,
SUM(IF(ExitStatus = 'NoAgentID', 1, 0)) AS NoAgentID,
SUM(IF(ExitStatus = 'Success', 1, 0)) AS Success
-- #TODO: Add others here when/if needed
FROM table
WHERE ...
GROUP BY TimePeriod
Do a first query to get all possible ExitStatus'es and then create your final query from your high-level programming language based on those results.
Use a special module for cross tabulation on your high-level programming language. For Perl, you have the SQLCrossTab module but I couldn't find one for PHP
Add another layer to your application by using OLAP (multi-dimensional views of your data) like Pentaho and then querying that layer instead of your original data
You can read a lot more about these solutions and an overall discussion of the subject
This is one way; you can use SUM() to count the number of items a particular condition is true. At the end you just group by the time as per normal.
SELECT DAY(time) AS TimePeriod,
SUM('NoAgentID' = exitStatus) AS NoAgentID,
SUM('Success' = exitStatus) AS Success, ...
FROM `table`
GROUP BY TimePeriod
Output:
4 1 3
5 1 5
The columns here are not dynamic though, which means you have to add conditions as you go along.
SELECT week(time) AS week,
SUM(ExitStatus = 'NoAgentID') AS 'COUNT(NoAgentID)',
SUM(ExitStatus = 'Success') AS 'COUNT(Success)'
FROM `table`
GROUP BY week
I'm making some guesses about how ExitStatus column works. Also, there are many ways of interpretting "week", such as week of year, of month, or quarter, ... You will need to put the appropriate function there.