MySQL self-join for an EAV based stock control application [duplicate] - mysql

This question already has answers here:
Zend Select with self join overwriting fields
(3 answers)
Closed 2 years ago.
This question relates to the schema I suggested in my original question regarding a stock control application.
I'm trying to create a MySQL query that provides the current stock for a particular item.
The query is working but I wondered whether there is a more efficient way of obtaining the information I require.
SELECT 's'.*,
'v1'.'attribute_id' AS 'att1',
'v1'.'value' AS 'val1'
'v2'.'attribute_id' AS 'att2',
'v2'.'value' AS 'val2'
FROM 'eav_ev' AS 'ev1'
INNER JOIN 'stock' AS 's' ON s.id = ev1.stock_id
INNER JOIN 'eav_ev' AS 'ev2' ON ev1.stock_id = ev2.stock_id
INNER JOIN 'eav_value' AS 'v1' ON v1.id = ev1.value_id
INNER JOIN 'eav_value' AS 'v2' ON v2.id = ev2.value_id
WHERE (ev1.entity_id = '45')
AND (ev1.value_id <> ev2.value_id)
AND (s.total > 0)
GROUP BY 'ev1'.'stock_id'
ORDER BY 'ev1'.'value_id' ASC
This returns something along the lines of
array (1) {
[0] => array(5) {
["stock_id"] => "2"
["att1"] => "3"
["val1"] => "M12"
["att2"] => "4"
["val2"] => "45"
}
}
It seems very messy but my poor brain is incapable of coming up with something better.
Any suggestions?

Instead of using attribute_id AS att1 you could also use value AS attribute_X if you store a list of attributes first. You can simply cache the query after which you can just select all needed data in 1 clear query.
Assuming you've fetched a list of attribute IDs first (i.e. SELECT attribute_id FROM eav_value), select this:
SELECT
v1.value_id AS attribute_1 -- (or whatever the ID was fetched in the first query)
v2.value_id AS attribute_2 -- (or whatever the second ID was fetched in the first query)
...

Related

How to retrieve, values, names and group names in one SQL query

I used to have an EAV shema with 4 tables in MySQl 5.7:
articles
article attributes
attribute names
attribute group names
After running into huge complexity, I learned from another question that this is not a good shema. So I got rid of table 2 where all the attributes have been stored and saved them either with values or value_ids directly into table one, as the STI model suggests.
Now I ended up with 3 tables:
articles
attribute names
attribute group names
At first it looked like it made my live easier, but while trying to replace a simple query that was getting all attribute group names and attribute names of a specific article I figured that this is also not ideal.
My previous query looked like this:
SELECT
cag.name_de,
cag.attr_group_id,
attr.attr_de,
attr.attr_id
FROM
articles_attr aa,
cat_attr attr,
cat_attr_groups cag
WHERE
aa.article_id = '181206'
AND aa.attr_id = attr.attr_id
AND cag.attr_group_id = attr.attr_group_id
Now with the new schema, I would need at least this:
Get all group names like e.g. "color"
SELECT
name_de,
attr_group_id
FROM
cat_attr_groups
Get all indirect values which have an ID like e.g. "green"
SELECT
attr.attr_group_id,
attr.attr_de
FROM
articles a,
cat_attr attr
WHERE
a.article_id = '181206'
AND (
(a.dial_c_id = attr.attr_id)
OR (a.dial_n_id = attr.attr_id)
OR (a.bracelet_color_id = attr.attr_id)
)
// pseudo code
$attr[$row->attr_group_id] = $row->attr_de;
Get all direct values:
SELECT
jewels,
vibrations
FROM
articles a
WHERE
a.article_id = '181206'
// pseudo code
$attr[4] = $row->jewels;
Map group names with group ids
foreach($attr AS $key => $value){
// somehow
}
This does not seem to be very elegant. How could I design my shema better or how could those queries be rewritten to retrieve the values in an acceptable query time?

update a row only if all values of anothher query respect a condition

let's say I have 4 tables
The requirements are:
If a supplier accepted an order then in the orders_suppliers table the order_supplier_status_id change to 3 .
If all suplliers associated to a specific combined order accepted their orders then in the Combined_orders table
the Combined_order_status_id change to 3.
If at least one supplier accepted an order and all order are not accepted than in the table Combined_orders the
Combined_order_status_id change to 2.
My question is: Is it possible in one query to update the Combined_order_status_id to accepted only if all suppliers accepted their orders ?
something like:
update Combined_orders
set Combined_orders.Combined_order_status_id = 3 if(all orders_suppliers.order_supplier_status_id == 3 )
otherwise
set Combined_orders.Combined_order_status_id = 2
where orders_suppliers.Combined_order_id = Combined_orders.Combined_order_id
Each time a supplier accept an order , I would like to execute this query.
For now I didn't find a way to do that in only one query. It is reaaly important for me to do that in one query , because from what I understand if is made in one query it would be an atomic operation.
You can use join and an aggregation:
update Combined_orders co join
(select os.combined_order_id, min(os.order_supplier_status_id) as min_ossi,
max(os.order_supplier_status_id) as max_ossi
from orders_suppliers os
group by os.combined_order_id
) os
on os.combined_order_id = co.combined_order_id
set co.Combined_order_status_id = (case when min_ossi = max_ossi and min_ossi = 3 then 3 else 2 end);

Rails Activerecord Relation: using subquery as a table for a SQL join

Can anybody help me figure out how to write the following SQL statement in Rails?
SELECT * FROM shifts a
INNER JOIN (
SELECT shifts.date, shifts.time, max(shifts.barber_id) as 'barber_id'
FROM shifts
WHERE shifts.date = '2017-09-06' and shifts.is_free = true
GROUP BY shifts.date, shifts.time
) b
ON a.date = b.date AND a.time = b.time AND a.barber_id = b.barber_id
WHERE a.`is_free` = true AND a.date = '2017-09-06'
Because where-conditional is the same in subquery and main query I'm using a scope as following:
scope :available, -> { where('shifts.is_free', true).where('shifts.date', '2017-09-06').order(date: :desc, time: :asc) }
I've also created the scope for subquery:
scope :grouped_shifts, -> { select("date, time, max(barber_id) as barber_id").group(:date, :time).available }
but I can't find out how to chain the scope to join method. Can I pass it as argument? Like that:
scope :something, -> { joins(self.grouped_shifts).available }
Or I shouldn't use scopes in this case?
Thanks for help in advance!
Piecing together what you have said in your question and in your first comment on your question, I gather you are trying to do this:
There are many barbers, each with many shifts. Each shift has a date and a time, and each barber may be available or not available on any given shift.
You want to find, for each time slot on 2017-09-06 that has at least one available barber, which barber available in that time slot has the largest id (ordered by time slot)
This is how you do it:
Shift.where(date: '2017-09-06').where(is_free: true).order(:time).group(:time).maximum(:barber_id)
You don't need to group by date, since all the shifts selected will be on the same date. If you wanted to extend this over all dates in the table, then it would look like this:
Shift.where(is_free: true).order(:date, :time).group(:date, :time).maximum(:barber_id)

NHibernate INNER JOIN on a SubQuery

I would like to do a subquery and then inner join the result of that to produce a query. I want to do this as I have tested an inner join query and it seems to be far more performant on MySql when compared to a straight IN subquery.
Below is a very basic example of the type of sql I am trying to reproduce.
Tables
ITEM
ItemId
Name
ITEMRELATIONS
ItemId
RelationId
Example Sql I would Like to create
Give me the COUNT of RELATIONs for ITEMs having a name of 'bob':
select ir.itemId, count(ir.relationId)
from ItemRelations ir
inner join (select itemId from Items where name = 'bob') sq
on ir.itemId = sq.itemId
group by ir.itemId
The base Nhibernate QueryOver
var bobItems = QueryOver.Of<Item>(() => itemAlias)
.Where(() => itemAlias.Name == "bob")
.Select(Projections.Id());
var bobRelationCount = session.QueryOver<ItemRelation>(() => itemRelationAlias)
.Inner.Join(/* Somehow join the detached criteria here on the itemId */)
.SelectList(
list =>
list.SelectGroup(() => itemRelationAlias.ItemId)
.WithAlias(() => itemRelationCountAlias.ItemId)
.SelectCount(() => itemRelationAlias.ItemRelationId)
.WithAlias(() => itemRelationCountAlias.Count))
.TransformUsing(Transformers.AliasToBean<ItemRelationCount>())
.List<ItemRelationCount>();
I know it may be possible to refactor this into a single query, however the above is merely as simple example. I cannot change the detached QueryOver, as it is handed to my bit of code and is used in other parts of the system.
Does anyone know if it is possible to do an inner join on a detached criteria?
MySql 5.6.5 has addressed the performance issue related to the query structure.
See here: http://bugs.mysql.com/bug.php?id=42259
No need for me to change the output format of my NHibernate queries anymore. :)

Linq Group on a multi-level object with select statement

I've got 3 dataset objects that are nested with each other using entity set objects. I am selecting the data like this
var newList = from s in MainTable
from a in s.SubTable1 where a.ColumnX = "value"
from b in a.Detail where b.Name = "searchValue"
select new {
ID = s.ID,
Company = a.CompanyName,
Name = b.Name,
Date = s.DueDate
Colour = b.Colour,
Town = a.Town
};
and this works fine, but the trouble is there are many records in the Detail object-list/table for each Name value so I get a load of duplicate rows and thus I only want to display one record per b.Name. I have tried putting
group s by b.Name into g
before the select, but then this seems to stop the select enabling me to select the columns I want (there are more, in practice). How do I use the group command in this circumstance while still keeping the output rows in a "flat" format?
Appending comment as answer to close question:-
Of course that if you group your results, you cant get select a column of a child, thats because there may be more than one childs and you have to specify an aggregate column for example the sum,max etx –