Fetching model objects with distinct column values - laravel

I have an applications table, containing user_id, email, phone, etc.. An applicant may or may not create a user account, because registration is optional; hence user_id is nullable. They might also apply multiple times with the same email address, in which case email in applications table would be duplicated across several records.
Now, the objective is to fetch all Application model instances excluding duplicates. EX: if Jack applied five times with jack#gmail.com, the applications table would have 5 records with email column set to jack#gmail.com. When fetching Applications, I'd like to get only one record with jack#gmail.com, the other four should be ignored.
So, I tried multiple approaches, but they either fetched all records or didn't execute at all:
groupBy('email') fails with get() because of non-aggreagated column email:
Illuminate\Database\QueryException with message 'SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'applications.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
At the same time, groupBy('email')->get(['email']) works well, but the problem is that I need other columns too (created_at at least), ideally - the full Application model instance.
Application::whereIn('id', function($query) { $query->select('id')->groupBy('email'); })->get() returns all records from the table
Application::select('*', 'DISTINCT email')->get() results in
Column not found: 1054 Unknown column 'DISTINCT email' in 'field list'
The Eloquent approach Application::distinct()->get(['email']) gives the proper list of emails, but once again, I need more columns that just the email. Once I start selecting more columns, I keep getting all records as results.
All of the following queries fail due to the strict mode being enabled:
SELECT * FROM ( SELECT * FROM applications ORDER BY id DESC ) AS tmp GROUP BY email
SELECT * FROM applications WHERE id IN(SELECT id FROM applications GROUP BY email)
SELECT email, id, created_at from ( SELECT email, id, created_at FROM applications) apps group by email
Can anyone help me to figure out the proper approach? I'd love to use Eloquent and work with actual models, rather than stdObj or other, if Eloquent can accommodate my problem/limitations.
P.S. The SQL engine I'm using is MySQL with strict mode enabled.

First of all, if you want to put an expression inside your eloquent you must use DB::raw('DISTINCT email') for that.
Second, I believe you should go with this approach:
Application::select('id', 'email', 'more_fields')->groupBy('email')->get();
You must have the column you group by it in your select() function.
Last option:
You can just use the ->distinct() function on your eloquent query to remove duplicates.

Related

joining tables with specialization-generalization relationship in oracle

I am trying to do a join with two tables. One is a supertype and one is a subtype. I am also trying to join it with another table and that works but the joining of the supertype and subtype is not. It says I have invalid identifier but he same id is in both so I am extremely confused why it's not working.
enter image description here
I included my code below and the attachment shows my tables. I have a table I am pulling first name and last name from and my other code is pulling from the supertype and subtype table.
SELECT first_name, last_name, gift_card_id, value_of_card
FROM gift_card
JOIN subject ON gift_card.subject_id = subject.subject_id
JOIN gift_card ON visa_gift_card.gift_card_id = gift_card.gift_card_id
GROUP BY gift_card.gift_Card_id
HAVING visa_gift_card.gift_card_id = gift_card.gift_card_id;
This is probably because you don't have first_name or last_name in any of your tables. Is there another table you should be joining on?
Also, I don't think group by/having is what you want, since you have no aggregate functions like sum in your select list.

How to select specific columns in a polymorphic relationship where table columns are different?

How can I only select specific columns in a polymorphic relationship because the table columns are different?
$query->with(['Postable' =>function($query){
$query->select('business_title','name ','last_name');
}
The business_title column exists in the agency table. Name and last_name exist in the user table. If I select one table other table gets an error of column not found.
Either develop a naming convention that fulfills your needs (ex: columns like 'name', 'title' can be found on many tables), or do not use select in your query and get all coulmns: $query->with(['Postable']); and you would need appropriate ways to read your query results. Or simply use multiple queries to read from different tables which seems to be the rational option.

Get latest row using Laravel?

It appear I am not getting latest row when rows have actually same created_at value.
Using $model->latest()->first() - I am getting first row rather than last row of created_at.
How to solve this?
latest() will use the created_at column by default.
If all of your created_at values are the exact same, this obviously won't work...
You can pass a column name to latest() to tell it to sort by that column instead.
You can try:
$model->latest('id')->first();
This assumes you have an incrementing id column.
This will entirely depend on what other data you have in your table. Any query of a relational database does not take the "physical position" into account - that is, there is no such thing as being able to get the "last inserted row" of a table until you are able check some value in the table that indicates it is the probably the last row.
One of the common ways to do this is to have a auto-incrementing unique key in the database (often the Primary Key), and you can simply get the largest value in that set. It's not guaranteed to be the last row inserted, but for most applications this is usually true.
What you need is the equivalent query to be executed
SELECT * FROM Table WHERE created_at = ? ORDER BY ID DESC LIMIT 1
or, in Eloquent ORM
$model->where('created_at', '=', ?)->orderBy('id', 'desc')->take(1)->first();
Keep in mind that you'll probably need other filters, since it is entirely possible other users or processes may insert records at the same time generating the same creation date, and you'll end up with somebody elses records.

Laravel firstOrCreate method throws duplicate ID error

I am in the midst of writing a command in my Laravel project which inserts categories to a database table, but depending on whether or not they already exist.
I investigated the way to do this and came across firstOrCreate method, so I wrote the following in my command:
$comCats = new CommunicationsCategories();
$comCats->firstOrCreate(
['name' => 'Job Updates'], ['region_id' => 1]);
$comCats->firstOrCreate(
['name' => 'Alerts'], ['region_id' => 1]);
Basically, I need to create these two categories in the communications_categories table with a region ID of 1. The Job Updates category already exists, so it skipped that as expected but when it tries to create the Alerts category which doesn't exist I get the following error in my console:
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value
violates unique constraint "communications_categories_pkey"
DETAIL: Key (id)=(2) already exists. (SQL: insert into
"communications_categories" ("name", "region_id", "updated_at",
"created_at") values (Alerts, 1, 2018-06-19 09:38:20, 2018-06-19
09:38:20) returning "id")
It appears that it's trying to allocate a primary key ID of 2 when it already exists - but the table structure has a nextval on the primary key which I thought would take the last ID added and create a new one after that. According to the Laravel documentation on Eloquent Inserts here there's no mention of having to stipulate the actual primary key id itself, and the fillable elements are only name and region_id.
Any help on this appreciated, as I'm reasonably new to Laravel and the eloqent database methods. If it helps, I'm using a PostgreSQL database.
The other answer seems to have assumed that you need to have firstOrCreate explained to you, when in fact you have actually encountered a bug in the Laravel framework and also assumed that Key (id)=(2) refers to region_id instead of your autoincrementing primary key id.
You most likely want a unique index on communications_categories.name.
You can instead use this SQL in postgres (assuming you have a unique index on name) which should be more reliable than firstOrCreate, but if you were planning to use other laravel features with firstOrCreate (like observers, or more query builders), their functionality could be lost.
INSERT INTO communications_categories (name, region_id, created_at, updated_at)
VALUES ('Job Updates', 1, NOW(), NOW())
ON CONFLICT (name) DO
UPDATE SET region_id = excluded.region_id,
updated_at = NOW()
RETURNING *;
which can be used like:
$values = DB::select($sql); // will return Array of StdClass
or without returning * if you don't need the id values.
DB::insert($sql); // will return true
If you need the Eloquent object returned, I would recommend including returning * or returning id and passing that to CommunicationsCategories::findOrFail($values[0]['id']). Putting that complicated insert statement into an Eloquent select will probably be a lot of work. Eloquent also has a function called hydrate which could work without additional SQL calls.
The SQL statement will insert the desired values, except when there is a conflict on the unique constraint, then which it will apply the values excluded by conflict from insert to the pre-existing rows via an update.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the attributes from the first parameter, along with those in the optional second parameter.
$comCats->firstOrCreate(
['name' => 'Job Updates'], ['region_id' => 1]);
here you are trying to locate first a name with "Job Updates" and a region_id with 1 ,if laravel cant find the specific data, it will try to create or insert the given parameters, but when the system tries to insert, region_id with value of "1" already exist.
if region_id is your primary key try:
$comCats->firstOrCreate(
['name' => 'Job Updates']);
Pardon me for I cannot post a comment yet...
If somebody encounter this same issue about postgresql, this is definitely a bug. In my case this bug occurred after I imported a local postgresql database using heroku following this procedure:
Workaround for pushing to heroku database on windows machince.
I tried running the query again and it worked... it fails sometimes too. Now this sucks. I'll update this I found another way around this issue.
Edit:
After looking at more searches about the issue, I found the following which perfectly matches my situation:
PostgreSQL: Unique violation: 7 ERROR: duplicate key value violates unique constraint “users_pkey”

How to rebuild data combination Power Query

In Power Query, I want to use a list of distinct values from one query (e.g. list of customers present on "Sales" table), to inject it on a SQL statement on another query (e.g. "Customer" dimensional table).
To pull the list of distinct values I have a function, getDistinct() that:
Retrieves one column from a query choice,
Only keep distinct values present on that column, and
Return these distinct values separated by commas so they can be injected within an SQL statement.
This function works fine on a standalone query. However, when I try to use it on my "Customer" query it throws an error (see code and error below):
let
Source = Oracle.Database("myServer", [Query="select * from db_customer where customer_id in (" & getDistinct(Sales,"CustomerID") & ")"])
in
Source
And the error:
Formula.Firewall: Query 'Customer' (step 'Source') references
other queries or steps, so it may not directly access a data source.
Please rebuild this data combination.
I've tried creating a different query that executes the function and then referencing it on my "Customer" query, but this doesn't seem to work. I know I can "Ignore Privacy Levels" (which by the way, I've checked and works), but since I don't know the implications of it, I'm afraid of leaked data.
I don't see why a function or any hand-written code is necessary for this requirement.
I would create a Query to get the Sales table and then Group by CustomerID. I would set that to: Load To / Only Create Connection.
Then the Customers Query would just be:
Source is Oracle Customers table
Merge to Sales Query on CustomerID, with Join Kind = Inner

Resources