TYPO3 doesn't respect field names in JOIN query - sql

I'm trying to fetch a JOIN query in TYPO3 using createQuery and $query->statement(...), but get odd results. Can someone explain to me why TYPO3 doesn't include table names as a prefix to column names in a JOIN query? Does this conflict with the ORM? Can I in anyway speed up a query of multiple 1:N-relations?
Example:
SELECT
client.name, project.name
FROM
client
LEFT JOIN
project ON project.client = client.uid
The PHP code from client repository:
$query = $this->createQuery();
$query->statement($statement);
$query->getQuerySettings()->setReturnRawQueryResult(true);
var_dump($query->execute());
The result prints out only names of the projects:
array (size=294)
0 =>
array (size=1)
'name' => string 'Projectname1' (length=21)
1 =>
array (size=1)
'name' => string 'Projectname2' (length=20)
2 =>
array (size=1)
'name' => string 'Projectname3' (length=32)
EDIT: This might be standard SQL behaviour.

Use aliases for fields:
SELECT
client.name client_name, project.name project_name
FROM
client
LEFT JOIN
project ON project.client = client.uid

Related

"andFilterWhere" work proper with "joinWith()" but not with "with()" in yii2

I am working in yii2.
There are employee and company table employee contains company_id.
I have a filter search running properly If I use joinWith()
$query = Employee::find();
$query->joinWith(['company']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
'sort' => false,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
//and below is the filterwhere
$query->andFilterWhere(['like', 'company.name', $this->company_id]);
But issue came when I make a query using with()
$query = Employee::find()->with(['company']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
'sort' => false,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
//when query contain with() then this filter is not working.
$query->andFilterWhere(['like', 'company.name', $this->company_id]);
This gives error when I use with()
Database Exception – yii\db\Exception
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'company.name' in 'where clause'
The SQL being executed was: SELECT COUNT(*) FROM `employee` WHERE `company`.`name` LIKE '%1%'
Here is the relation in employee with company:
public function getCompany(){
return $this->hasOne(Company::className(), ['id'=> 'company_id']);
}
Can anyone help me or guide me how could I filter data properly using with() in a query?
Thanks.
You can't swap joinWith() and with() methods when you need to filter by the column from the related table. That's because these methods does completely different things.
Methods like joinWith() and join() actually modifies the query to add the "JOIN" part to the SQL query. The with in joinWith allows you to specify the joined table by the relation definition in the model. The eager loading in joinWith is only side effect and you can even turn that off by passing false as second parameter.
When you do:
Employee::find()->joinWith(['company'])->all();
The query that is run looks like:
SELECT * FROM employee LEFT JOIN company ON (...)
On the other side the method with() doesn't modify the query itself. It only forces the eager loading of related models. In reality the second query is used for preloading the related records.
When you do:
Employee::find()->with(['company'])->all();
It actually runs queries like these:
SELECT * FROM employee;
SELECT * FROM company WHERE id IN (...company ids selected in first query...);
So when you try to do:
$query = Employee::find()
->with(['company'])
->andFilterWhere(['like', 'company.name', $this->company_id])
->all();
The generated query is
SELECT * FROM employee WHERE company.name LIKE ...

CakePHP - add custom SQL to a Query object

I'm using CakePHP 3.5.13 to build an application which has 4 separate databases.
The main database (Datasource default in config/app.php) for the application has been baked. It was a legacy database, and the naming conventions are not written according to the way CakePHP specifies. Nonetheless it works, after going through the Models and editing things.
In a controller I have the following:
$substances = TableRegistry::get('Substances');
$query = $substances->find()->limit(250)->offset(0);
$query->select(['id', 'app_id', 'name']);
$query->contain([
'Cas' => [
'sort' => ['Cas.id' => 'ASC']
]
]);
$query->contain([
'Ecs' => [
'sort' => ['Ecs.id' => 'ASC']
]
]);
If I var_dump($query) I get an SQL string as follows:
SELECT Substances.id AS `Substances__id`,
Substances.app_id AS `Substances__app_id`,
Substances.name AS `Substances__name`
FROM substances Substances LIMIT 250 OFFSET 0
I need to modify this so the query contains an INNER JOIN to a table which is stored in one of the other databases (Datasource sdb5_tmpdata in config/app.php). The SQL I need is as follows:
SELECT Substances.id AS `Substances__id`,
Substances.app_id AS `Substances__app_id`,
Substances.name AS `Substances__name`,
Substances.date AS `Substances__date`
FROM substances Substances
INNER JOIN `sdb5_tmpdata`.`searching_1745` AS tf
ON tf.id = Substances.id LIMIT 250 OFFSET 0;
The difference between the above query and original is the following SQL:
INNER JOIN `sdb5_tmpdata`.`searching_1745` AS tf
ON tf.id = Substances.id
I don't have a corresponding Model for the table 'searching_1745' because these tables are dynamically created (and later dropped) on a database which holds "temporary" user data.
Is it possible to modify the query object, $query, such that I can introduce the custom SQL that does the inner join?
I have tried $query = $query->newExpr()->add('INNER JOIN 'sdb5_tmpdata'.'searching_1745' AS tf ON tf.id = Substances.id'); but it doesn't work.
the query builder let you build the query as you like it. It does not matter if you don't have a Table Object for that table
$query->join([
'tf ' => [
'table' => 'sdb5_tmpdata.searching_1745',
'type' => 'INNER',
'conditions' => 'tf.id = Substances.id',
]);
see here
I don't know if you can modify the query object, but tou can always write your own custom queries:
use Cake\Datasource\ConnectionManager;
$conn = ConnectionManager::get('default');
$query = $conn->query('query goes here');
for more info, read: https://book.cakephp.org/3.0/en/orm/database-basics.html#executing-queries

join with AND id=1 with SQL::Abstract::More

I'm trying to do a join using SQL::Abstract::More that has an `and and then a literal value, not on a table column.
=>{table.table_id=table_id,table_log.date>table.date,table_log.event_id=1}
gd_audit_log
the resulting output that I want
LEFT OUTER JOIN table_log ON (
table_log.date > table.date
AND table.table_id = table_log.table_id
AND table_log.event_id = 1
)
this code works except for
AND table_log.event_id = 1
the error is
... failed: Unknown column 'table_log.1' in 'on clause'
obviously it's generating the wrong SQL, what I'm trying to figure out is how to get it to generate the SQL I need.
From RT Bug 84972. To insert a literal value, you need to use the hashref syntax, instead
of the string syntax :
my $result = $sqla->join(
'table',
{ operator => '=>',
condition => { '%1$s.table_id' => {-ident => '%2$s.table_id'},
'%2$s.date' => {'>' => {-ident => '%1$s.date'}},
'%2$s.event_id' => 1}},
'table_log'
);
Seems to me that table_log.event_id = 1 isn't a valid join clause, but should be in a where clause.
Use force Luke
qw/table
=>{table.table_id=table_id,table_log.date>table.date,table_log.event_id='1'}
table_log/
need 'escape' 1 by single quote

Convert Sql query with Group by and Having clause to Linq To Sql query in C#

I need help to convert following ql query to Linq to Sql query.
select Name, Address
from Entity
group by Name, Address
having count(distinct LinkedTo) = 1
Idea is to find all unique Name, Address pairs who only have 1 distinct LinkedTo value. Remember that there are other columns in the table as well.
I would try something like this:
Entity.GroupBy(e => new { e.Name, e.Address})
.Where(g => g.Select(e => e.LinkedTo).Distinct().Count() == 1)
.Select(g => g.Key);
You should put a breakpoint after that line and check the SQL that is generated to find what is really going to the database.
You could use:
from ent in Entities
group ent by new { ent.Name, ent.Address } into grouped
where grouped.Select(g => g.LinkedTo).Distinct().Count() == 1
select new { grouped.Key.Name, grouped.Key.Address }
The generated SQL does not use a having clause. I'm not sure LINQ can generate that.

Selecting from a table where field = this and value = that

I have a mysql table that looks something like this:
Row 1:
'visitor_input_id' => int 1
'name' => string 'country'
'value' => string 'Canada'
Row 2:
'visitor_input_id' => int 1
'name' => string 'province'
'value' => string 'Alberta'
Row 3:
'visitor_input_id' => int 1
'name' => string 'first_name'
'value' => string 'Jim'
The problem is that I need to be able to filter it so that a user can generate reports using this:
filter 1:
'field_name' => string 'country'
'field_operator' => string '='
'field_value' => string 'Canada'
filter 2:
'field_name' => string 'province'
'field_operator' => string '!='
'field_value' => string 'Alberta'
filter 3:
'field_name' => string 'first_name'
'field_operator' => string '%LIKE%'
'field_value' => string 'Jim'
I am not really sure what the query would look like to be able to select from this using the filters. Any suggestions? (Unfortunately, creating a new table to store the data more sanely is not really feasible at this time because it is already full of user data)
I think it would look something like this:
if(field_name = 'province' THEN ADD WHERE field_value != 'Alberta')
if(field_name = 'country' THEN ADD WHERE field_value = 'Canada')
if(field_name = 'first_name' THEN ADD WHERE field_value LIKE '%jim%')
but I am not sure how that would work...
Turns out that this seems to work:
SELECT * FROM visitor_fields
INNER JOIN visitor_inputs ON (visitor_inputs.input_id = visitor_fields.input_id)
INNER JOIN visitor_fields as filter_0
ON (filter_0.input_id=visitor_inputs.input_id
AND filter_0.field_name = 'province'
AND filter_0.field_value != 'Alberta')
INNER JOIN visitor_fields as filter_1
ON (filter_1.input_id=visitor_inputs.input_id
AND filter_1.field_name = 'country'
AND filter_1.field_value = 'Canada')
INNER JOIN visitor_fields as filter_2
ON (filter_2.input_id=visitor_inputs.input_id
AND filter_2.field_name = 'first_name'
AND filter_2.field_value LIKE '%jim%')
I know you say creating a new table with a better schema isn't feasible, but restructuring the data would make it more efficient to query and easier to work with. Just create a new table (called visitor in my example). Then select from the old table to populate the new visitor table.
vistor
----------------
vistor_id
firstname
province
country
You could loop through the statement below with any scripting language (PHP, TSQL, whatever scripting language you're most comfortable with). Just get a list of all vistor_id's and loop through them with the sql below, replacing the x with the visitor_id.
INSERT INTO visitor (visitor_id, name, province, country) VALUES X,
(SELECT value FROM old_table WHERE name='first_name' AND vistor_id = x),
(SELECT value FROM old_table WHERE name='province' AND vistor_id = x),
(SELECT value FROM old_table WHERE name='country' AND vistor_id = x);
This will produce a table where all a visitor's data is on a single row.
Are you able to create an SQL string and then execute it? The string would look like this:
SELECT * FROM yourtable
WHERE (name='country' AND value='Canada') AND
(name='province' AND value!='Alberta') AND
(name='first_name' AND value LIKE '%jim%)
EDIT:
I see. Multiple records. So try joining them. This is not correct SQL syntax but should look similar:
SELECT * FROM
(SELECT * FROM yourtable WHERE (name='country' AND value='Canada'))
JOIN on visitor_input_id
(SELECT * FROM yourtable WHERE (name='province' AND value!='Alberta'))
JOIN on visitor_input_id
(SELECT * FROM yourtable WHERE (name='first_name' AND value LIKE '%jim%))