I can select from database by passing dynamic variables like :Type and :Code using the following statement:
SELECT "MSG" as "MESSAGE" FROM "TABLENAME" WHERE "TYPE" = :Type AND "CODE" = :Code
However, let say I want to pass in the variable conditionally for Type. If in the database, the Type column has All value, then choose the All value, else if the Type column shows other values, then replace the :Type with the passed in arguments. Something like this:
SELECT "MSG" as "MESSAGE" FROM "TABLENAME"
WHERE
IF "TYPE" = "All"
"TYPE" = "All"
ELSE
"TYPE" = :Type
AND "CODE" = :Code
What is the correct syntax for this scenario?
For something with a limited number of cases (two in your example), I would simply use two different SQL statements and use JavaScript logic to decide which one to execute.
You could also explore using a CASE statement in the WHERE CLAUSE or maybe some kind of UNION?
Related
I would like to check whether certain values are contained at a certain index in the array.
With the following statement I can check whether "Value1" is contained at position "1":
'''$myArray.get(1).contains('value1')'''
What if I want to check at position "1" whether "Value1" or "Value2" are included (or more than two).
I would like to avoid creating multiple conditions with OR links in the Watson Assistant Node.
If you don't want to create multiple conditions you can use regex.
String[] myArray = {"Value1", "Value2", "Value3"};
myArray[1].matches("(Value1|Value2|Value3)"); <-- will return true
You can basically add as much values as you want as a parameter to the matches() function.
So in the end your SpEL expression should look something like this:
"#myArray[1].matches('(Value1|Value2|Value3)')"
The data coming into the warehouse is structured like this
{"Client ID":"1234567","client_name":"Kareem" }
when I use the function
JSON_EXTRACT_PATH_TEXT(COLUMN_NAME, 'Client ID')
it give me this error :
Invalid extraction path 'Client ID': invalid token at position 7.
is there another workaround to get the value for this key ? or for using this key with the function to get the value out of the JSON column ?
Per its documentation, the JSON_EXTRACT_PATH_TEXT function follows the standard notation for object keys. Keys that carry spaces are required to be double-quoted, and the same applies here. The following works for your example:
JSON_EXTRACT_PATH_TEXT(COLUMN_NAME, '"Client ID"')
COLUMN_NAME:"Client ID"
worked for me
The following does work, which is supposed to be close to equivalent. My example assumes your values are a string, since that is the input of the function you were trying to use:
WITH x AS (SELECT '{"Client ID":"1234567","client_name":"Kareem" }' as json_string)
SELECT parse_json(json_string):"Client ID"
FROM x;
I am generating a case statement to return either 0 or 1:
$desc_case = $q->newExpr()
->addCase(
[$q->newExpr()->add(["description IS" => NULL])],
[0,1],
["integer","integer"]
);
$q = $q->select(["has_desc" => $desc_case]);
Which results in the following correct SQL:
SELECT [fields removed for clarity], (CASE WHEN (description) IS NULL THEN :c0 ELSE :c1 END) AS `has_desc` FROM skills Skills
I've turned hydration off, and retrieved the result with
->hydrate(false)->toArray();
The result of the CASE statement is returned as a string- either "0" or "1" - which is messing up logic downstream.
I've traced the execution code as best I can, and it looks like CakePHP is using the type names passed to correctly bind the values, but nowhere does the type make its way into the TypeMap used for mapping output.
An easy workaround is to adjust the values after the fact (which I'm doing), but I'd like to make this work as expected on principle... :)
The type information passed to addCase() is only ment to be used for input casting, ie the values passed will be bound as the given types. The return values, ie the values being selected via the compiled CASE statement, are in no way being affected.
If you want to affect the type used for casting selected values of columns that do not exist in the schema (note that changing the schema will also affect other parts of the ORM and the query builder), then you have to change the type map accordingly, for example:
$query
->getSelectTypeMap()
->addDefaults([
'has_desc' => 'integer'
]);
See also
Cakephp-3.x: How to change the data type of a selected alias?
I use TranslationBehavior to handle translated data in my app. When retrieving data, CakePHP returns the translated string or if this string is not available, the value of the default locale. To request the data, the following query is used:
$items = $this->Model->find('all')
->all()
->extract('name');
To order the output, the query is:
$items = $this->Model->find('all')
->order([$this->Model->translationField('name') => 'ASC'])
->all()
->extract('name');
This works for all default locale items and all translated items. But when the translation for a record is missing, it will break the order. In this case the correct fallback value is returned, but the order is no longer correct. The output looks like this:
['A... (Translated)', 'B... (Translated)', 'A... (Default)', 'C... (Default)']
What I expect is the following order:
['A... (Default)', 'A... (Translated)', 'B... (Translated)', 'C... (Default)']
To achieve this, I changed the query to:
$items = $this->Model->find('all')
->order(['IF('.$this->Model->translationField('name').' != "", '.$this->Model->translationField('name').', Model.name)' => 'ASC'])
->all()
->extract('name');
Which gives the expected order:
['A... (Default)', 'A... (Translated)', 'B... (Translated)', 'C... (Default)']
The question is: is this the correct way to handle the order of mixed locales? Or did I miss something and CakePHP already provides a simpler solution?
You may ask, why should we mix the locales? In my case, it's not necessary to translate all the strings, because some of the items are identical in both languages.
Looking at how CakePHP queries the translated fields, and merges them later on at PHP level, this is the expected behavior, and you'd indeed have to use a conditional expression in the ORDER clause.
I'd suggest to use a CASE expression, as the IF() function is MySQL specific, something like:
$query = $this->Model->find();
$query
->orderAsc(
$query->newExpr()->addCase(
[
$query->newExpr()->isNotNull($this->Model->translationField('name'))
],
[
$query->identifier($this->Model->translationField('name')),
$query->identifier('Model.name')
]
)
)
// ...
Which would generate an expression similar to:
ORDER BY
CASE WHEN Model_name_translation.content IS NOT NULL
THEN Model_name_translation.content
ELSE Model.name END
ASC
And as mentioned in the comments, If you go the "do not translate identical strings" route, then you should avoid storing empty strings for such "missing" translations, instead do not store a record for them at all.
In order to avoid all this you could of course add translations for everything, even if they are equal in the different languages.
I'm trying to get a query working using a case statement, and can't figure out how to get the case to return a column value instead of a constant. I have the query working perfectly, except that the column names I'm providing for the results are being quoted or otherwise mishandled by Cake or maybe PDO somewhere down in a layer that I can't dig my way through. I got as far down as bindValue, but none of the documentation I encountered along the way tells me how to do this.
I have found this example comment:
$statement->bindValue(1, 'a title');
$statement->bindValue(2, 5, PDO::INT);
$statement->bindValue('active', true, 'boolean');
$statement->bindValue(5, new \DateTime(), 'date');
but in all these cases, the value provided is a constant. I need to pass in a string that is the name of the column that I want returned.
I tried both 'string' (resulted in quoted column name) and 'integer' (resulted in 0). I tried PDO::FETCH_COLUMN (seemed highly unlikely, but looked like the next best bet from http://php.net/manual/en/pdo.constants.php, and easy to try it...). I tried 'literal', inspired by the way you can put literal strings into expressions (resulted in Error: unknown type "literal"). That error message led me to src/Database/Type.php, but nothing in there helped me either.
So, I'm pretty much stumped. Here's a simple version of the code I have (leaving out a couple of conditions and unrelated columns):
$query = $this->Games->find();
$team_id = $query->newExpr()->addCase(
[$query->newExpr()->eq('Games.status', 'home_default')],
['home_team_id', 'away_team_id'],
['string', 'string']
);
$defaulting = $query
->select([
'id' => $team_id,
'count' => 'COUNT(Games.id)',
])
->where([
'Games.status IN' => ['home_default', 'away_default'],
])
->group('id')
->toArray();
This generates this SQL:
SELECT
(CASE WHEN Games.status = 'home_default'
THEN 'home_team_id' ELSE 'away_team_id' END) AS `id`,
COUNT(Games.id) AS `count`
FROM games Games
WHERE Games.status in ('home_default','away_default')
GROUP BY id
Note that THEN 'home_team_id' ELSE 'away_team_id' END should be simply THEN home_team_id ELSE away_team_id END. This will then allow me to read the list of ids of teams that have defaulted games along with the number of games they defaulted.
By default the values passed to the second argument of QueryExpression::addCase() are being treated as to be converted to literal values, not as identifiers. If you need the latter, then you should use an expression, an IdentifierExpression.
use Cake\Database\Expression\IdentifierExpression;
// ...
$team_id = $query->newExpr()->addCase(
[
$query->newExpr()->eq('Games.status', 'home_default')
],
[
new IdentifierExpression('Games.home_team_id'),
new IdentifierExpression('Games.away_team_id')
]
);
Also ditch the third argument in this case, you don't want the values to be string literals (for expressions the types would be ignored anyways).