just a quick question here.
Lets say I have a stored procedure for seleting records from a table.
select * from tbl_table
where deleted = #deleted
So for this above, I must pass in a parameter being either TRUE or FALSE.
Is it possible to return results where deleted is equal to TRUE and FALSE but retaining the option to pass in a parameter. So I guess, if no parameter is passed in, there is no filter.
The other way I thought was to do this..
select * from tbl_table
where deleted = #deleted1
and deleted = #deleted2
So you have 2 parameters for the same filter. This way you can do true or false or set both filters the same - giving more leeway.
If anyone has any ideas or thoughts on this that would be great!
set your parameter as nullable (= NULL) and then
select * from tbl_table
where (#deleted IS NULL) OR (deleted = #deleted)
Then do not supply the parameter (or explicitly supply as NULL), when you do not want the filter by that parameter to be applied.
(But be aware this can sometimes have parameter sniffing consequences, especially with a large number of parameters)
Related
I have the following XML and I need to update the specified one based on the parameter:
<DOPremium>
<BasePremium>337500</BasePremium>
<TotalPremium>337500</TotalPremium>
<NettPremium>337500</NettPremium>
<GrossPremium>337500</GrossPremium>
<OptionId>0</OptionId>
</DOPremium>
<DOPremium>
<BasePremium>337500</BasePremium>
<TotalPremium>337500</TotalPremium>
<NettPremium>337500</NettPremium>
<GrossPremium>337500</GrossPremium>
<OptionId>1</OptionId>
</DOPremium>
<DOPremium>
<BasePremium>337500</BasePremium>
<TotalPremium>337500</TotalPremium>
<NettPremium>337500</NettPremium>
<GrossPremium>337500</GrossPremium>
<OptionId>2</OptionId>
</DOPremium>
I'm trying to update the respective nodes based on the selection of the DOPremium object, but I'm not able to do that. Can someone verify where I'm wrong?
SET #NewXmlValue = N' <BasePremium>[sql:variable("#R15_premium")]</BasePremium>'
SET #DataXml.modify('delete /*/Premiums/DOPremium/BasePremium[sql:variable("#OptionID")]')
SET #DataXml.modify('insert sql:variable("#NewXmlValue") into (/*/Premiums/DOPremium[sql:variable("#OptionID")])[1]')
-- Add TotalPremium
SET #NewXmlValue = N' <TotalPremium>[sql:variable("#R15_premium")]</TotalPremium>'
SET #DataXml.modify('delete /*/Premiums/DOPremium/TotalPremium[sql:variable("#OptionID")]')
SET #DataXml.modify('insert sql:variable("#NewXmlValue") into (/*/Premiums/DOPremium[sql:variable("#OptionID")])[1]')
OK, first of all, this can't work:
SET #NewXmlValue = N'<BasePremium>[sql:variable("#R15_premium")]</BasePremium>'
sql:variable() is only interpreted as a function in an XQuery operation. This isn't an XQuery operation, so it will just textually insert sql:variable(...). If you want an actual XML node with the text value of the variable, you have to be a little more roundabout:
SET #NewXmlValue = '';
SET #NewXmlValue = (SELECT #NewXmlValue.query('<BasePremium>{sql:variable("#R15_premium")}</BasePremium>'));
This approach (and others) can be found in the docs. (In this very simple case concatenating the strings in T-SQL also works, of course, but in general that's not a good idea because it doesn't take care of escaping the XML when necessary.)
The syntax for selecting the desired DOPremium node also needs work -- /BasePremium[sql:variable("#OptionID")] is legal, but it means "the BasePremium node that, sequentially numbering from 1, has number #OptionID". If #OptionID is supposed to match what's in OptionID, that's not the way to write it.
If your intent was to write "change the contents of the BasePremium value of the node with OptionID text equal to #OptionID to the value #R15_premium", here's how you do that (well, one way to do that):
SET #DataXml.modify('
replace value of (
/*
/Premiums
/DOPremium[child::OptionId/.=sql:variable("#OptionID")]
/BasePremium
/text()
)[1]
with sql:variable("#R15_premium")')
And something similar for TotalPremium. You can, of course, also replace entire nodes, but that seems unnecessary here.
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?
This is a textual description of data for which I need to create a database design (using SQLite) for an application.
The application needs to keep a record of operations. Each operation has a Name and its list of parameters. Each parameter has its Name and a Value. However, the values of the parameters will change over the lifetime of the app (in fact the user will be able to changes them using GUI) and we want to keep a history of the values which a certain parameter has had. Furthermore, each operation can have multiple parameter sets. A parameter set is like an envelope which encompasses a set of parameter values (which all belong to the same operation) and gives this envelope a unique Number and a non-unique Description.
This is what I have so-far:
[Database model image][1]
The database model should allow me to perform these actions on the database data:
Show a list of operations - I know how to do this.
Show a list of parameters for a given operation - I know how to do this.
For a given operation, show all its parameters as columns and show the values of the parameters as rows - each row represents a different parameter value from the history of values. I'm stuck at this one.
For a given operation, show a list of all parameter sets which belong to that operation. I'm stuck at this one too.
For a given operation and for a given parameter set, get the latest values of its parameters. Stuck at this.
I'm not sure if I should re-work my database model or if I should look for proper SQL statements to accomplish the tasks above with the model that I have. Any help is greatly appreciated. Thank you.
EDIT 1
I have re-worked my database model according to a helpful advice from #Marek Herman. Thanks to that I am able to accomplish tasks 1) 2) 4).
Now I'm trying to accomplish 5) which should not be that difficult with the current database model. I have this SQL statement:
SELECT Parameter.ParameterIdentifier, ParameterValue.ParameterValue,
ParameterValueVersion.VersionNumber, ParameterValueVersion.ChangedOn
FROM ParameterValueVersion INNER JOIN
(((Operation INNER JOIN Parameter ON Operation.OperationPLC_ID = Parameter.OperationPLC_ID)
INNER JOIN ParameterSet ON Operation.OperationPLC_ID = ParameterSet.OperationPLC_ID)
INNER JOIN ParameterValue ON (ParameterSet.ID = ParameterValue.ParameterSetID) AND
(Parameter.ID = ParameterValue.ParameterID)) ON ParameterValueVersion.ID = ParameterValue.ParameterValueVersionID
WHERE (Operation.OperationPLC_ID=[opID] AND
ParameterSet.ParameterSetNumber=[parSetNum]);
where [opID] and [parSetNum] are the input parameters. This SQL statement actually only joins all these tables together on their PK->FK relationship: Operation, Parameter, ParameterSet, ParameterValue, ParameterValueVersion and filters the rows by specified OperationPLC_ID and ParameterSetNumber.
Here is an example of an output of this SQL statement. Each row shows a name of a parameter, its value, a version number of the value and date of change of that value. Some parameters only have one value (only one version -e.g., "OFFSET"). Some parameters have two values. For example "PREFILLING" has a value of "3" which was input on Oct 20, 2016 (and has a version number 1) and it also has a value of "3.5" which was input on Oct 21, 2016 and has a version number of 2. So I'd like to show only the latest versions of the values of the parameters. Any advice how to modify the SQL statement is much appreciated. Thank you.
EDIT 2
I guess I figured out how to perform 5). I had to study a bit how GROUP BY works. This did the trick:
SELECT Parameter.ParameterIdentifier, last(ParameterValue.ParameterValue) AS ParameterValue, last(ParameterValueVersion.ChangedOn) AS ChangedOn, max(ParameterValueVersion.VersionNumber) AS VersionNumber
FROM ParameterValueVersion INNER JOIN
(((Operation INNER JOIN Parameter ON Operation.OperationPLC_ID = Parameter.OperationPLC_ID)
INNER JOIN ParameterSet ON Operation.OperationPLC_ID = ParameterSet.OperationPLC_ID)
INNER JOIN ParameterValue ON (ParameterSet.ID = ParameterValue.ParameterSetID) AND
(Parameter.ID = ParameterValue.ParameterID)) ON ParameterValueVersion.ID = ParameterValue.ParameterValueVersionID
WHERE (((Operation.OperationPLC_ID)=[opID]) AND ((ParameterSet.ParameterSetNumber)=[parSetNum]))
GROUP BY Parameter.ParameterIdentifier
ORDER BY Parameter.ParameterIdentifier
Now I still need to figure out how to perform task no. 3. I'm gonna study the suggested COALESCE function. Thank you.
0) I would connect ParameterSet to Operation and Parameter and not to ParameterValue.
1) okay!
2) okay!
3) I think you can use the COALESCE() function to display the columns and then it should be possible to show all parameters with matching OperationID
4) you can do that if you do point #0
5) same as above I think
I have to retrieve the db entry. I know the query I have written returns only one row. Therefor I am using getSingleResult. However when the query returns a null, I am not able to catch it. How do I solve this?
Here is the piece of code I have
try {
result = em.createQuery("SELECT d FROM fields d WHERE d.fieldID = :fieldID", Field.class)
.setParameter("fieldID", fieldID)
.getSingleResult();
//manual null check only seems to work. But it seems tedious to check every DB column for null value :(
if((result.getValsText() == null)){
result = new Field();
result.setValText("empty");
}
} catch (NoResultException e) {
result = new Field();
result.setValText("empty");
}
Please advise.
Thanks
For non-Id attributes you can either
send a COUNT query before your regular query or better
call getResultList() on your Query
getResultList will return an empty list if no results have been found. To get your single result, check whether the list is empty and call get(0) on it.
If the attribute is the #Id attribute, call entityManager.find(MyEntity.class, id); - find returns null if no results have been found and a single instance of your entity if it has been found.
EDIT- the last option is preferable for reasons of perfomance and readability. Use the second option (the list) where you cannot use the last one.
SELECT * FROM Feedback WHERE text =! None
Nul, doesn't work either.
It doesn't work... So how should I write this query?
From GAE documentation:
It is not possible to query for entities that are missing a given property. One alternative is to create a fixed (modeled) property with a default value of None, then create a filter for entities with None as the property value.
You could achieve the same results by:
def notnulls():
return [z for z in db.GqlQuery('SELECT * FROM Feedback') if z.text]
This will return a list of Feedback objects where the text field is not None. Although this does have the extra overhead of loading all Feedback objects first.
Entities with null are not included in the index for that query. You may want to actually store a dummy value like 'None'/'Null' instead. (Ref. http://code.google.com/appengine/docs/python/datastore/queries.html#Restrictions_on_Queries )
Try this:
select * from Feedback where text > ''
Found reference here. Note that it's undocumented, so maybe not a very good solution.