Random Query Zend - random

I search lot but not getting any help on random records with mysql.I am using random query to get random data from database with not in with some limit. If database contains 100 records and suppose I am giving 30 as limit with random function and some not in id.My problem is that when it hit first time I am giving not in id as empty and it is giving me 30 records randomly.But for second time except last 30 records i.e(it is in not in variable) it should give me another 30 records from 70 records but it is giving me less than 30 i.e(28,29).This because first it applies random function and limit and then filter it with not in.My query is like below:
$subQuery = $this->select()
->from(array('s' => 'shop'), array('s.shop_id','b.shop_name','b.shop_template_id'))
->order('RAND()')
->where('s.shop_id NOT in (?)', $shop_id)
->limit(30);
$query = $this->select()
->from(array('b' => $subQuery), array('*'))
->join(array('p' => 'product'), 's.shop_id = p.shop_id', array('p.product_price'))
->setIntegrityCheck(false);
$resultRows = $this->fetchAll($query);
Update: I got the problem why it is giving (28,29) records sometime because join query contains product for shop and if some shop is having 0 products it does not get that shop.My question is that irrespective of product how can i get that shop from database.

Your problem is that you join instead of left join. Also there's no need for a sub select. This should work:
$objSelect = $this->select()->setIntegrityCheck(false);
$objSelect->from(
array('s' => 'shop'),
array('s.shop_id','b.shop_name','b.shop_template_id')
);
$objSelect->joinLeft(
array('p' => 'product'),
's.shop_id = p.shop_id',
array('p.product_price')
);
$objSelect->order('RAND()');
$objSelect->limit(30);
$objRowSet = $this->fetchAll($objSelect);

Related

Laravel: How to get duplicated records and group them together?

The code below is what I have to get all the duplicated products (by title) and group them together. It works perfectly fine. However, I so many records in my Products table and getting all of them causes a performance issue. Is there a way this could be optimised to avoid getting all records and group them in one query? Thank you.
$products = Product::all();
$groupsOfProducts = $products->groupBy('title');
$duplicatedProductsGrouped = [];
foreach($groupsOfProducts as $productGroup) {
$productIsDuplicated = $productGroup->count() > 1;
if($productIsDuplicated) {
$duplicatedProductsGrouped[] = $productGroup;
}
}
var_dump($duplicatedProductsGrouped);
You can use having in the group by:
Product::groupBy('title')->having(DB::raw('count(*)'), ">", "1")->select('title')->get()
And you will get the titles of the duplicates, then you can query the database with those titles
EDIT:
Please also try and see if this is faster
Product::getQuery()->whereIn('title', array_column( DB::select('select title from products group by title having count(*) > 1'), 'title'))->get();
with this line you will get ONLY the products that has a duplicate title, and so your Collection groupby should be faster to aggregate the records by the title
Let your database do the work. When you call Product::all(), you're getting every single record, then making PHP do the rest. Change your query to something like the following:
Product::selectRaw("title, COUNT(*) AS count")->groupBy("title")->get();
The result will be a Collection of Product instances with a title and count attribute, which you can access and determine duplicated ones:
$products = Product::selectRaw("title, COUNT(*) AS count")->groupBy("title")->get();
$duplicatedProducts = collect([]);
foreach($products AS $product){
if($product->count > 1){
$duplicatedProducts->push($product);
}
}
dd($duplicatedProducts);

Can we get the total record count and the first record only in the same eloquent query?

Can we get the total record count and the first record only in the same eloquent query?
I know we can get the total records via below eloquent queries:
1. Model::count();
2. Collection Method
Model::all()->count();
And we can get the first record like this:
Model::first();
I have one solution for the same i.e.
$data = Model::all();
1. $count = count($data);
2. $count = $data->count();
And for the first row/data
1. $firstRecord = $data[0];
2. $firstRecord = $data->first();
When you create a query, Laravel under the hood, creates a new instance of the query builder with the method newQuery(), this query can be reused as much times as you want
$query = Model::where('...')->whereHas('...')->orderBy('...');
[
'count' => $query->count(),
'collection' => $query->get(),
'first' => $query->first()
]
Something similar happens with the LengthAwarePaginator's paginate() method. Have a look at the source code.

How to get top 10 customers based on values of orders

I have a list of Customers who each have a list of Orders. Each Order has a list of LineItems.
I would like to write a LINQ query that would get me the top 10 customers based on order value (i.e. money spent) and not the total number of orders.
One customer could have 2 orders but could have spent £10,000, but another customer could have 100 orders, and only spent £500.
Right now, I have this which gets me the top 10 customers by the number of orders.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let activeCount = c.SaleOrders.Count(so => so.Status != SaleOrderStatus.Cancelled)
orderby activeCount descending
select c).Take(10);
UPDATE
Thanks to Jon Skeet's comment about doing a double Sum, I wrote the following query which compiles.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let orderSum = c.SaleOrders.Where(so => so.Status != SaleOrderStatus.Cancelled)
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
orderby orderSum descending
select c).Take(10);
But when I run this, I get the following error:
It seems LINQ doesn't recognise my .CalculateTotal() method which sit on my LineItem.cs entity.
The problem you were seeing is that CalculateTotal() is not something that Linq can translate into SQL (which is done at run-time, hence no complier error).
The essential problem here is that Linq doesn't really work on lambdas (Func<>), but actually Expressions (Expression<Func<>>), which is the code in a partial compiled state, which Linq then goes about disassembling and translating into SQL.
So, let assume CalculateTotal is a member function defined like this:
public decimal CalculateTotal()
{
return this.quantity * this.value;
}
We could define that as a local lambda function
Func<LineItem, decimal> CalculateTotal = (li => li.quantity * li.value);
Now, we have a lambda which takes a LineItem and returns a value, which is exactly what Sum() wants, so now we can replace:
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
with
.Sum(so => so.LineItems.Sum(CalculateTotal))
But that will crash, just as it did before, because, as I said, it wants an Expression. So, we give it one:
Expression<Func<LineItem, decimal>> CalculateTotal = (li => li.quantity * li.value);

Eloquent to count with different status

It's possible to make one query to get total, sold & unsold in laravel eloquent?
$total_apple = Item::whereName('Apple')->count();
$sold_apple = Item::whereName('Apple')->whereStatus(2)->count();
$unsold_apple = Item::whereName('Apple')->whereStatus(1)->count();
Yes you can totally do that. You can use filter method on collection object returned by your Eloquent query.
$apples = Item::whereName('Apple')->get();
$soldApples = $apples->filter(function ($apple){
return $apple->status == 2;
});
$unsoldApples = $apples->filter(function ($apple){
return $apple->status == 1;
});
$soldApples and $unsoldApples contains the object of the items. You can then just use count($soldApples) and count($unsoldApples) to get their count.
filter method is against the collection object so there is no sql overhead.
There is no need run multiple queries or even fetch the entire results and use collection methods to loop through. Just use raw queries.
$apples = Item::whereName('Apple')
->selectRaw('COUNT(*) as total_apples,
SUM(status=2) as sold_apples,
SUM(status=1) as unsold_apples')
->first();
echo $apples->total_apples; // Outputs total apples
echo $apples->unsold_apples; // Outputs the unsold apples
echo $apples->sold_apples; // Outputs the sold apples
Since you are only doing simple counts though, you can use the query builder as well.
I would get all the items in one collection, then run the where statement on that collection. This should trigger a single Query.
$apples = Item::whereName('Apple')->get(); // This goes against SQL
$total_apple = $apples->count(); //This runs on the Collection object not SQL
$sold_apple = $apples->whereStatus(2)->count();
$unsold_apple = $apples->whereStatus(1)->count();

Count with a basic query

I´m doing a paging query with a simple filter, it´s working like a charm.
var result = client.Search<MyMetaData>(
x => x.Index("MyIndex")
.Type("MyType")
.QueryString(filtro)
.From(from)
.Size(size)
);
But I need to know the number of results without paging to inform users.
I´m trying to do with the Count method, but without success.
In ES you can use the "Size" field to limit the number of records returned but the "Total" field will always have the correct total on the server even if only 100 records are returned (as with my sample below).
var result = ElasticClient.Search<PackingConfigES>(x =>
x.Size(100)
.MatchAll()
);
var totalResults = result.Total;

Resources