Zend_Db join query - database

I am trying to apply join query on 3 tables in Zend_Db.My query is as follow:
$id_array = array("1","2");
$query = $this->select();
$query->from(array('b' => 'brands'), array('b.brand_id','b.brand_name'))->where('b.brand_id NOT in (?)', $id_array)->order('RAND()')->limit(5);
$query->join(array('p' => 'product'), 'b.brand_id = p.brand_id', array('p.product_id', 'p.product_price'));
$query->join(array('pimg' => 'product_img_map'), 'p.product_id = pimg.product_id', array('pimg.img_location'));
$query->setIntegrityCheck(false);
$resultRows = $this->fetchAll($query);
return $resultRows;
In that one brand may have more than one product,but in query I am applying limit to brand table i.e 5. By default it is applied to product also because if one brand having 5 products it is only giving information of one brand.Is there any suggestion on this.
Thanks.

You could use a sub-query to obtain the desired results:
$id_array = array("1","2");
$subQuery = $this->select()
->from(array('b' => 'brands'), array('b.brand_id','b.brand_name'))
->where('b.brand_id NOT in (?)', $id_array)
->order('RAND()')
->limit(5);
$query = $this->select()
->from(array('b' => $subQuery), array('*'))
->join(array('p' => 'product'), 'b.brand_id = p.brand_id', array('p.product_id', 'p.product_price'))
->join(array('pimg' => 'product_img_map'), 'p.product_id = pimg.product_id', array('pimg.img_location'));

Related

Is there any way to fix Illegal offset type error when i try to update the content of pivot table in laravel

I'm trying to update the data of pivot table (i want to change product in the order)
I have :
-product table:
id
name
product_price
created_at
updated_at
-order table:
id
status
user_id
created_at
updated_at
-order_products table:
order_id
product_id
product_price
count
created_at
updated_at
in Order model:
public function product(){
return $this->belongsToMany('App\Product')->withPivot('count');
}
in Prodect model:
public function order(){
return $this->belongsToMany('App\Order');
}
in controller:
public function EditOrder(Request $request , $id){
$order = Order::find($id);
$order->status = $request->status;
$order->user_id = $request->user_id;
$order->update();
$product_ids = $request->products;
$selectedItems = [];
foreach ( $product_ids as $key => $productId) {
$id1 = $key['product_id'];
$product = Product ::find($id);
$count = $key['count'];
$selectedItems[$productId] = ['product_id' => $id1 ];
$selectedItems[$productId] = ['count' => $count];
$selectedItems[$productId] = ['order_id' => $order->id];
$selectedItems[$productId] = ['price' => $product->product_price];
$order->product()->updateExistingPivot($selectedItems);
}
return response()->json([
'message' => 'Success'
],200);
i faced this error :
Illegal offset type in file C:\Users\Acer\Documents\GitHub\easyrest\app\Http\Controllers\ResturantController.php on line 80
I want to update product_id and count in order_products
updateExistingPivot() expects 2 params, the first being the id of the relationship you want to update, the second being the array of columns you wish to update. You're not passing an id, so it's trying to use your array as the id, which is why you're getting the error. Fix your logic:
foreach($product_ids as $key => $productId) {
$id1 = $key['product_id'];
$product = Product::find($id);
$count = $key['count'];
$selectedItems[$productId] = ['product_id' => $id1];
$selectedItems[$productId] = ['count' => $count];
// $selectedItems[$productId] = ['order_id' => $order->id]; // [1]
$selectedItems[$productId] = ['price' => $product->product_price];
$order->product()->updateExistingPivot($productId, $selectedItems[$productId]); // [2]
}
[1]: You can omit order_id; $order->product()->updateExistingPivot() already knows the order_id and you're not updating it, so it's redundant.
[2]: You don't want to pass the entire $selectedItems array, only the sub-array contained in $selectedItems[$productId]
By passing $productId as the first argument of updateExistingPivot(), Laravel knows what row you want to update, and will use the array keys in $selectedItems[$productId] to determine which columns to update to what. I don't know if this will actually update product_id, but if it doesn't, you may need to detach() then attach() the data instead:
foreach($product_ids as $key => $productId) {
$selectedItems[$id1] = ['count' => $count];
$selectedItems[$id1] = ['price' => $product->product_price];
$order->products()->detach($productId);
$order->products()->attach($id1, $selectedItems[$id1]);
}

EF optimize multiple order by select top 1

I have the following Entity Framework query:
var queryResponse = await db.DataPoints.GroupBy(x => x.Device).Select(x => new
{
lon = x.Key.DataPoints.OrderByDescending(y => y.DateTime).Select(y => y.Longitude).FirstOrDefault(),
lat = x.Key.DataPoints.OrderByDescending(y => y.DateTime).Select(y => y.Longitude).FirstOrDefault(),
date = x.Key.DataPoints.OrderByDescending(y => y.DateTime).Select(y => y.DateTime).FirstOrDefault(),
label = x.Key.Name,
})
.OrderByDescending(x => x.label)
.ToListAsync();
Now you can see in my select, I have to get lon,'lat', and 'date'. However the way im doing it, I have to orderby and select the first one 3 times. The 'DataPoints' is a very large table.
in C# i would normally do the orderBy once and just select the entire object, and then later on break it up into the 3 properties. However in this case I want SQL to return the exact fields.
is there a more efective way to write this query?
Try this:
var queryResponse =
(from g in db.DataPoints.GroupBy(x => x.Device)
let latestDataPoint = g.Key.DataPoints.OrderByDescending(p => p.DateTime)
.FirstOrDefault()
select new
{
lon = latestDataPoint.Longitude,
lat = latestDataPoint.Latitude,
date = latestDataPoint.DateTime,
label = g.Key.Name
})
.OrderByDescending(x => x.label)
.ToList();

Converting SQL Server query containing GROUP BY into NHibernate LINQ

I'm basically trying to retrieve a paged list of unique GUIDs, sorted by (row) creation date.
I've been able to draft a SQL Server query that seems to work for me based on this answer, but now I have to translate that into LINQ.
SELECT TOP 15 payment.ClientRef,
MAX(payment.CreatedDateUtc)
FROM PaymentTransactionState payment
INNER JOIN OrderState orderstate ON payment.ClientRef = orderstate.ClientRef
WHERE orderstate.UserId = 2 AND
payment.PaymentState IN (
'Rejected',
'Authorized')
GROUP BY payment.ClientRef
ORDER BY MAX(payment.CreatedDateUtc) DESC,
payment.ClientRef
Problem is, I can't apply GroupBy on an IQueryOver, I'm probably missing the appropiate syntax:
session
.QueryOver<Payment>()
.JoinAlias(orderState => orderState.OrderStateEntity, () => orderStateRow)
.Where(() => orderStateRow.UserId == customer.UserId)
.WhereRestrictionOn(payment => payment.PaymentState).IsIn(paymentStates)
.GroupBy(pts => pts.ClientRef)
.OrderBy(payment => payment.CreatedDateUtc).Desc
.Skip(pageIndex*pageSize)
.Take(pageSize)
.List();
I could probably do the group by in query syntax, but I'm not so sure about the Skip & Take bit.
I would try like this:
var query = db.PaymentTransactionState
.Where( pts => pts.OrderState.UserId == 2 &&
new string[] {"Rejected", "Authorized"}.Contains(pts.PaymentState) )
.GroupBy( pts => pts.ClientRef )
.OrderByDescending( pts => pts.Max( p => p.CreatedDateUtc))
.ThenBy( p => p.Key )
.Take(15);
So here's what worked for me: basically I had to use SelectList instead of GroupBy; SelectGroup, SelectMax & TransformUsing were easy to tackle once I found that;
PaymentRow paymentAlias = null;
OrderStateRow orderStateRow = null;
var transactionStateRows = session
.QueryOver<PaymentRow >()
.JoinAlias(orderState => orderState.OrderStateEntity, () => orderStateRow)
.Where(() => orderStateRow.UserId == customer.UserId)
.WhereRestrictionOn(payment => payment.PaymentState).IsIn(paymentStates)
.SelectList(list => list
.SelectGroup(payment => payment.ClientRef).WithAlias(() => paymentAlias.ClientRef)
.SelectMax(payment => payment.CreatedDateUtc).WithAlias(() => paymentAlias.CreatedDateUtc))
.TransformUsing(Transformers.AliasToBean<PaymentRow >())
.OrderBy(payment => payment.CreatedDateUtc).Desc
.Skip(pageIndex*pageSize)
.Take(pageSize)
.List();
I'll leave this here in case someone might find my travails useful in the future. Thank you for your replies.

Yii2 ignore first database result

is there any way to ignore the first result record in Yii2 at a query? I have a list of numbers that represents a client. For designing purposes i had to query the first record separatly but now i have it duplicated. My questin is how can I query in Yii2 to ignore the first result?
Regards,
Gábor
The second find is the query where i need to ignore the first result:
public function actionGeneratePage() {
public function actionGeneratePage() {
$behivott = Sorszam::find()->with('ablak')
->orderBy(['behivas_datum' => SORT_DESC])
->limit(1)
->all();
$sorszamok = Sorszam::find()->with('ablak')
->orderBy(['behivas_datum' => SORT_DESC])
->limit(4)
->all();
$reklam = Reklam::find()->all();
return $this->render('generatePage', [
'sorszamok' => $sorszamok,
'reklam' => $reklam,
'behivott' => $behivott,
]);
}
You use offset() to skip the first record:
$sorszamok = Sorszam::find()->with('ablak')
->orderBy(['behivas_datum' => SORT_DESC])
->limit(4)
->offset(1)
->all();
Also you can use a single query to get both $behivott and $sorszamok with array_shift:
$sorszamok = Sorszam::find()->with('ablak')
->orderBy(['behivas_datum' => SORT_DESC])
->limit(5)
->all();
$behivott = array_shift($sorszamok);

Sum of multiple queries to optimise number of queries performed

I have a query which must sum the values from several tables and add the result. The system is simply an inventory system and I'm trying to get the stock level by calculating incomings (deliveries), outgoings (issues) and adjustments to items.
As the stock level is a calculated value (sum(deliveries) - sum(issues)) + sum(adjustments) I am trying to create a function that will get this value with a minimal number of queries.
At current I have linq that performs three separate queries to get each summed value and then perform the addition/subtraction in my function, however I am convinced there must be a better way to calculate the value without having to do three separate queries.
The current function is as follows:
public static int GetStockLevel(int itemId)
{
using (var db = EntityModel.Create())
{
var issueItemStock = db.IssueItems.Where(x => x.ItemId == itemId).Sum(x => x.QuantityFulfilled);
var deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
var adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
return (deliveryItemStock - issueItemStock) + adjustmentsStock;
}
}
In my mind the SQL query is quite simple, so I have considered a stored procedure, however I think there must be a way to do this with linq.
Many thanks
Edit: Answer
Taking the code from Ocelot20's answer, with a slight change. Each of the lets can return a null, and if it does then linq throws an exception. Using the DefaultIfEmpty command will negate this, and return a 0 for the final calculation. The actual code I have used is as follows:
from ii in db.Items
let issueItems = db.IssueItems.Where(x => x.ItemId == itemId).Select(t => t.QuantityFulfilled).DefaultIfEmpty(0).Sum()
let deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId).Select(t => t.Quantity).DefaultIfEmpty(0).Sum()
let adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId).Select(t => t.Quantity).DefaultIfEmpty(0).Sum()
select (deliveryItemStock - issueItems) + adjustmentsStock);
Without knowing what your entities look like, you could do something like this:
public static int GetStockLevel(int itemId)
{
using (var db = EntityModel.Create())
{
// Note: Won't work if there are no IssueItems found.
return (from ii in db.IssueItems
let issueItems = db.IssueItems.Where(x => x.ItemId == itemId)
.Sum(x => x.QuantityFulfilled)
let deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId)
.Sum(x => x.Quantity)
let adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId)
.Sum(x => x.Quantity)
select issueItems + deliveryItemStock + adjustmentsStock).FirstOrDefault() ?? 0;
}
}
I tested a similar query on my own db and it worked in a single query. I suspect that since they all have a common ItemId, that using entity relations could make this look something like:
// Ideal solution:
(from i in db.Items
where i.Id == itemId
let issueItems = i.IssueItems.Sum(x => x.QuantityFulfilled)
let deliveryItemStock = i.DeliveryItems.Sum(x => x.Quantity)
let adjustmentsStock = i.Adjustments.Sum(x => x.Quantity)
select issueItems + deliveryItemStock + adjustmentsStock).SingleOrDefault() ?? 0;
Have you considered adding a view to the database that performs the calculations that you can then just use a simple select query (or SP) to return the values that you need?
I reckon this should work and the SQL generated is not particularly complex. If you think there is something wrong with it let me know and I will update my answer.
public static int GetStockLevel(int itemId)
{
using (var db = EntityModel.Create())
{
return db.IssueItems.Where(x => x.ItemId == itemId).GroupBy(x => x.ItemId)
.GroupJoin(db.DeliveryItems, x => x.First().ItemId, y => y.ItemId, (x, y) => new
{ Issues = x, Deliveries = y})
.GroupJoin(db.Adjustments, x=> x.Issues.First().ItemId, y=> y.ItemId, (x, y) => new
{
IssuesSum = x.Issues.Sum(i => i.QuantityFullfilled),
DeliveriesSum = x.Deliveries.Sum(d => d.Quantity),
AdjustmentsSum = y.Sum(a => a.Quantity)})
.Select(x => x.IssuesSum - x.DeliverysSum + x.AdjustmentsSum);
}
}

Resources