how to group the fields inside in some addselect in querybuilder? - json

what I want is simple, I want to group specific fields of a query with querybuilder in 3 different levels of relationship with an alias, to be clearer, I have 3 related tables, consulta (medical consultation), paciente(pacient), doctor (doctor), usuario (user), so with all what I get in my query is the medical consultation with its pacient data, plus doctor data and with its related user data, what I want is to omit irrelevant fields within those related tables, I'm using addSelect to include all the data among the levels of relationship but what I get is all the fields, I want to retrieve specific fields of the "paciente" table, and in the user table (which is the user related of the doctor), I will show how the scheme would be:
Consulta :{
// omit the other fields
{paciente: name and last name}
{doctor: { usuario: name and last name}}}
$queryBuilder = $em->getRepository('MedicoBundle:Consulta')
->createQueryBuilder('e')
->leftJoin('e.paciente', 'a')
->addSelect('a')
->leftJoin('e.doctor', 'o')
->addSelect('o')
->leftJoin('o.usuario', 'u')
->addSelect('u')
->where('e.fecha =:fecha')
->orderBy('e.id', 'ASC')
->setParameter('fecha', $fecha)
->getQuery();
it retrieves all the fields which is not so bad, but I want to retrieve is some of them and omit others as I show in the scheme

Just select what you want instead of selecting everything :
$queryBuilder = $em->getRepository('MedicoBundle:Consulta')
->createQueryBuilder('e')
->leftJoin('e.paciente', 'a')
->addSelect('a.name, a.lastname')
->leftJoin('e.doctor', 'o')
->addSelect('o.name, o.lastname')
->leftJoin('o.usuario', 'u')
->where('e.fecha =:fecha')
->orderBy('e.id', 'ASC')
->setParameter('fecha', $fecha)
->getQuery();
Though I do not know how you called your name and lastname attribute, so be careful with it.

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?

When i try to display the count value of one column it counts the no. of arrays stored in a single cell of the same row on the same table

I have written the following function to display a table consisting of
recipe name, description, cusine , ........ ..., ingredients AND LIKES.
Everything works fine but the likes column doesnot show the no. of likes bu instead sows the no. of ingredients of the recipe
public function rec(){
$ing = inglist::all();
$rcusine=recipecusine::all();
$rtype=recipetype::all();
$rec = DB::table('recipe_list')
->select('recipe_list.recipe_id','recipe_list.Recipe_name',
'recipe_list.Recipe_desc','recipe_list.Recipe_duration',
DB::raw('group_concat(ing_list.Ing_name separator ",") as recipe_ingredients'),
'recipe_cusine.Cusine_name', 'recipe_type.Recipe_type_name','recipe_list.image',
DB::raw('count(likerecipes.likecount) as likes'))
->join('recipe_inglist', 'recipe_list.recipe_id','=','recipe_inglist.Recipe_id')
->join('ing_list', 'recipe_inglist.Ing_id','=','ing_list.ing_id')
->join('recipe_cusine', 'recipe_list.Recipe_cusine_id','=','recipe_cusine.cusine_id')
->join('recipe_type', 'recipe_list.Recipe_type_id','=','recipe_type.Recipe_typeID')
->join( 'likerecipes', 'recipe_list.recipe_id', '=', 'likerecipes.recipe_id')
->where('recipe_list.recipe_id','>=','1')
->groupBy('recipe_list.recipe_id', 'recipe_list.recipe_name','recipe_list.recipe_desc','recipe_list.recipe_duration', 'recipe_cusine.Cusine_name','recipe_type.Recipe_type_name','recipe_list.image' )->get() ;
/* var_dump($rec);
die();*/
return view('recipe', ['ingredients'=>$ing, 'cusine'=>$rcusine, 'type'=>$rtype,'recipe'=>$rec]);
}
the output i get is
and this is my likerecipes table
can anyone help me where i am wrong.
Because of the joins, your data is duplicated through the records. Since the likerecipes table seems to store like votes casted one by one identified by an id field uniquely, I would count the distinct likerecipes.id values per group:
'count(distinct likerecipes.id) as likes'
I would also introduce the distinct to the group_concat() to make sure that multiple like votes do not make the same ingridients appear multiple times:
'group_concat(distinct ing_list.Ing_name separator ",") as recipe_ingredients'

SQLAlchemy foreign keys mapped to list of ids, not entities

In the usual Customer with Orders example, this kind of SQLAlchemy code...
data = db.query(Customer)\
.join(Order, Customer.id == Order.cst_id)\
.filter(Order.amount>1000)
...would provide instances of the Customer model that are associated with e.g. large orders (amount > 1000). The resulting Customer instances would also include a list of their orders, since in this example we used backref for that reason:
class Order:
...
customer = relationship("customers", backref=backref('orders'))
The problem with this, is that iterating over Customer.orders means that the DB will return complete instances of Order - basically doing a 'select *' on all the columns of Order.
What if, for performance reasons, one wants to e.g. read only 1 field from Order (e.g. the id) and have the .orders field inside Customer instances be a simple list of IDs?
customers = db.query(Customer)....
...
pdb> print customers[0].orders
[2,4,7]
Is that possible with SQLAlchemy?
What you could do is make a query this way:
(
session.query(Customer.id, Order.id)
.select_from(Customer)
.join(Customer.order)
.filter(Order.amount > 1000)
)
It doesn't produce the exact result as what you have asked, but it gives you a list of tuples which looks like [(customer_id, order_id), ...].
I am not entirely sure if you can eagerly load order_ids into Customer object, but I think it should, you might want to look at joinedload, subqueryload and perhaps go through the relationship-loading docs if that helps.
In this case it works you could write it as;
(
session.query(Customer)
.select_from(Customer)
.join(Customer.order)
.options(db.joinedload(Customer.orders))
.filter(Order.amount > 1000)
)
and also use noload to avoid loading other columns.
I ended up doing this optimally - with array aggregation:
data = db.query(Customer).with_entities(
Customer,
func.ARRAY_AGG(
Order.id,
type_=ARRAY(Integer, as_tuple=True)).label('order_ids')
).outerjoin(
Orders, Customer.id == Order.cst_id
).group_by(
Customer.id
)
This returns tuples of (CustomerEntity, list) - which is exactly what I wanted.

mysql search with multiple conditions

Need a bit of help writing query as I have database containing 1000's records of records.
Basically I have a database that contains the following fields
entryID
date
toothNumber
procedureName
studentName
tutorName
isolationSkill
isolationKnowledge
cavitySkill
cavityKnowledge
matrixSkill
matrixKnowledge
restorativeSkill
restorativeKnowledge
I want to write a query that searches all the records for a particular name(for example "Joe Bloggs") and the procedureName contains "Class II"
On top of that I want it to return the amount of times the Values N, B and C appear in the isolationskill - restorativeKnowldge columns.
So in the end I can see a list like this
Hope this is making sense. Let me know if you require any more information.
Thanks in advance
I think something like this would give you what you want, without you posting what you would like to see, kinda hard to tell, but this will pop out all the rows just like normal, and then give you a count field for each of the given Value n, B, C fields. Apply this syntax multiple times to get the exact results you are looking for on the different fields.
SELECT
entryID,
date,
procedurename,
studentName,
tutorName,
restorativeSkill,
isolationKnowledge,
cavitySkill,
cavityKnowledge,
matrixSkill,
matrixKnowledge,
restorativeknowledge,
SUM(IF(isolationSkill = 'N', 1,0)),
SUM(IF(restorativeKnowldge = 'B', 1,0)) FROM records
WHERE procedureName = 'Class II' and Name = "Joe Bloggs";

SQL issue: one to many relationship and EAV model

Good evening guys,
I'm a newbie to web programming and I need your help to solve a problem inherent to SQL query.
The database engine I'm using is MySQL and I access it via PHP, here I'll explain a simplified version of my database, just to fix ideas.
Let's suppose to work with a database containing three tables: teams, teams_information, attributes. More precisely:
1) teams is a table containing some basic information about italian football teams (soccer, not american football :D), it is formed by three fields: 'id' (int, primary key), 'name' (varchar, team name), nickname (Varchar, team nickname);
2) attributes is a table containing a list of possible information about a football team, such as city (the city where team plays its home match), captain (team captain's fullname), f_number (number of fans) and so on. This table is formed by three fields: id (int, primary key), attribute_name (varchar, an identifier for the attribute), attribute_desc (text, an explanation of the meaning of attribute). Each record of this table represents a single possible attribute of a football team;
3) teams_information is a table where some information, about teams listed in team table, are available. This table contains three fields: id (int, primary key), team_id (int, a foreign key which identifies a team), attribute_id (int, a foreign key which identifies one of the attributes listed in attributes table), attribute_value (varchar, the value of the attribute). Each record represents a single attribute of a single team. In general, different teams will have a different number of information, so for some teams a large number of attributes will be available while for other teams only a small number of attributes will be available.
Note that relation between teams and teams_information is one to many and the same relation exists between attributes and teams_information
Well, given this model my purpose is to realize a grid (maybe with ExtJS 4.1) to show user the list of italian football team, each record of this grid will represent a single football team and will contain all possible attributes: some fields may be empty (because, for considered team, the correspondent attribute is unknown), while the others will contain the values stored in teams_information table (for the considered team).
According to the above grid's field are: id, team_name and a number of fields to represent all the different attributes listed in 'attributes' table.
My question is: can I realize such a grid by using a SINGLE SQL query (maybe a proper SELECT query, to fetch all data I need from database tables) ?
Can anyone suggest me how to write a similar query (if it exists) ?
Thanks in advance for helping me.
Regards.
Enrico.
The short answer to your question is no, there is no simple construct in MySQL to achieve the result set you are looking for.
But it is possible to carefully (painstakingly) craft such a query. Here is an example, I trust you will be able to decipher it. Basically, I'm using correlated subqueries in the select list, for each attribute I want returned.
SELECT t.id
, t.name
, t.nickname
, ( SELECT v1.attribute_value
FROM team_information v1
JOIN attributes a1
ON a1.id = v1.attribute_id AND a1.attribute_name = 'city'
WHERE v1.team_id = t.id ORDER BY 1 LIMIT 1
) AS city
, ( SELECT v2.attribute_value
FROM team_information v2 JOIN attributes a2
ON a2.id = v2.attribute_id AND a2.attribute_name = 'captain'
WHERE v2.team_id = t.id ORDER BY 1 LIMIT 1
) AS captain
, ( SELECT v3.attribute_value
FROM team_information v3 JOIN attributes a3
ON a3.id = v3.attribute_id AND a3.attribute_name = 'f_number'
WHERE v3.team_id = t.id ORDER BY 1 LIMIT 1
) AS f_number
FROM teams t
ORDER BY t.id
For 'multi-valued' attributes, you'd have to pull each instance of the attribute separately. (Use the LIMIT to specify whether you are retrieving the first one, the second one, etc.)
, ( SELECT v4.attribute_value
FROM team_information v4 JOIN attributes a4
ON a4.id = v4.attribute_id AND a4.attribute_name = 'nickname'
WHERE v4.team_id = t.id ORDER BY 1 LIMIT 0,1
) AS nickname_1st
, ( SELECT v5.attribute_value
FROM team_information v5 JOIN attributes a5
ON a5.id = v5.attribute_id AND a5.attribute_name = 'nickname'
WHERE v5.team_id = t.id ORDER BY 1 LIMIT 1,1
) AS nickname_2nd
, ( SELECT v6.attribute_value
FROM team_information v6 JOIN attributes a6
ON a6.id = v6.attribute_id AND a6.attribute_name = 'nickname'
WHERE v6.team_id = t.id ORDER BY 1 LIMIT 2,1
) AS nickname_3rd
I use nickname as an example here, because American soccer clubs frequently have more than one nickname, e.g. Chicago Fire Soccer Club has nicknames: 'The Fire', 'La Máquina Roja', 'Men in Red', 'CF97', et al.)
NOT AN ANSWER TO YOUR QUESTION, BUT ...
Have I mentioned numerous times before, how much I dislike working with EAV database implementations? What should IMO be a very simple query turns into an overly complicated beast of a potentially light dimming query.
Wouldn't it be much simpler to create a table where each "attribute" is a separate column? Then queries to return reasonable result sets would look more reasonable...
SELECT id, name, nickname, city, captain, f_number, ... FROM team
But what really makes me shudder is the prospect that some developer is going to decide that the LDQ should be "hidden" in the database as a view, to enable the "simpler" query.
If you go this route, PLEASE PLEASE PLEASE resist any urge you may have to store this query in the database as a view.
I'm going to take a slightly different route. Spencer's answer is fantastic, and it addresses the issue quite well, but there's still a large underlying problem.
The data that you are trying to display on the site is over-normalized in the database. I won't elaborate, since, again, Spencer's answer highlights the issue pretty well.
Rather, I'd like to recommend a solution that denormalizes the data a bit.
Convert all of your Team data into a single table with many columns. (If there is Player data that isn't covered in the question, that would be a second table, but I'll gloss over that for now.)
Sure, you'll have a whole bunch of columns, and a lot of the columns might be NULL for a lot of the rows. It's not normalized, and it's not pretty, but here's the huge advantage that you gain.
Your query becomes:
SELECT * FROM Teams
That's it. That gets displayed right to the website and you are done. You might have to go out of your way to realize this schema, but it would be totally worth the time investment.
I think what you're saying is that you want the rows in the attributes table to appear as columns in the result recordset. If this is correct, then then in SQL you would use PIVOT.
A quick search on SO seems to indicate that there is no PIVOT equivalent in MySql.
I wrote a simple PHP script to generalize spencer's idea to solve my issue.
Here's the code:
<?php
require_once('includes/db.config.php'); //this file performs connection to mysql
/*
* Following function requires a table name ($table)
* and a number of service fields ($num). Given those parameters
* it returns the number of table fields (excluding service fields).
*/
function get_fields_number($table,$num,$conn)
{
$query = "SELECT * FROM $table";
$result = mysql_query($query,$conn);
return mysql_num_fields($result)-$num; //remember there are $num service fields
}
/*
* Following function requires a table name ($table) and an array
* containing a list of service fields names. Given those parameters,
* it returns the list of field names. That list is contained within an array and
* service fields are excluded.
*/
function get_fields_name($table,$service,$conn)
{
$query = "SELECT * FROM $table";
$result = mysql_query($query,$conn);
$name = array(); //Array to be returned
for ($i=0;$i<mysql_num_fields($result);$i++)
{
if(!in_array(mysql_field_name($result,$i),$service))
{
//currently selected field is not a service field
$name[] = mysql_field_name($result,$i);
}
}
return $name;
}
//Below $conn is db connection created in 'db.config.php'
$query = "SELECT `name` FROM `detail_arg` WHERE visibility = 0";
$res = mysql_query($query,$conn);
if($res===false)
{
$err_msg = mysql_real_escape_string(mysql_error($conn));
echo "{success:false,data:'".$err_msg."'}";
die();
}
$arg = array(); //list of argument names
while($row = mysql_fetch_assoc($res))
{
$arg[] = $row['name'];
}
//Following function writes the select subquery which is
//necessary to build a column containing a single attribute.
function make_subquery($attribute) //$attribute contains attribute name
{
$query = "";
$query.="(SELECT incident_detail.arg_value ";
$query.="FROM incident_detail ";
$query.="INNER JOIN detail_arg ";
$query.="ON incident_detail.arg_id = detail_arg.id AND detail_arg.name='".$attribute."' ";
$query.="WHERE incident.id = incident_detail.incident_id) ";
$query.="AS $attribute";
return $query;
}
/*
echo make_subquery("date"); //debug code
*/
$subquery = array(); //list of subqueries
for($i=0;$i<count($arg);$i++)
{
$subquery[] = make_subquery($arg[$i]);
}
$query = "SELECT "; //final query containing subqueries
$fields = get_fields_name("incident",array("id","visibility"),$conn);
//list of 'incident' table's fields
for($i=0;$i<count($fields);$i++)
{
$query.="incident.".$fields[$i].", ";
}
//insert the subqueries
$sub = implode($subquery,", ");
$query .= $sub;
$query.=" FROM incident ORDER BY incident.id";
echo $query;
?>