How to compare strings in mybatis 3 with if statement - dynamic sql - ibatis

In myBatis 3 how do you compare a string when using dynamic sql?
With iBatis previously you could do the following:
<isEqual property="sortBy" compareValue="portfolio_id">order by p.portfolio_id</isEqual>
Now with myBatis can you do the following:
<if test="sortBy.equals('facility_id')">
order by pd.facility_id
</if>
sortBy is a property in the parameter map and "facility_id" is the value
I'm a little bit confused as it was straight forward in ibatis.

All you have to do is
<if test="sortBy == 'facility_id' ">
order by pd.facility_id
</if>

Related

Using variable arrays in models

Is it possible to define an array in the vars section and use it inside the SQL syntax of a model?
Something like this
dbt_project.yml:
vars:
active_country_codes: ['it','ge']
model.sql
SELECT ...
FROM TABLE WHERE country_code IN ('{{ var("active_country_codes") }}')
I've tried with a single value, i.e:['it'], and works but if I add another it starts failing.
I am using the SQL Server Data connector.
The query that you are writing is correct. You just need to pass the variable as a string with a comma also as a string character.
vars:
active_country_codes: 'it'',''ge'
You can do something like this :
SELECT ...
FROM TABLE WHERE country_code IN ('{{ var("active_country_codes") }}')
And it will create query for you like this:
SELECT ...
FROM TABLE WHERE country_code IN ('it,'ge')
I have tested this and it's working fine. I'm using Bigquery Connection but it shouldn't matter as it's dbt generation.
My educated guess is that the result of {{ var("active_country_codes") }} is to insert a comma separated string. In that case, you'll need a string splitting function. You will have to roll your own if you haven't already, unless you have SQL Server 2016 or later. Then you can use string_split. Below is code using it. I use the exists approach as opposed to in due to performance.
select ...
from table t
where exists (
select 0
from string_split('{{ var("active_country_codes") }}', ',') ss
where t.country_code = ss.value
)
I would use:
vars:
var_name: "'one','two','three'"
where field_name in ({{ var("var_name") }})
Looks a little bit clearer than:
active_country_codes: 'it'',''ge'

SQL to match the content of a nested XML node

I have a database field containing XML nested like this:
<configuration>
<modules>
<genericMailer>
<moduleContent>
<data>
<sendMethod>Email</sendMethod>
My XML can contain many instances of the <genericMailer> element (and it's subnodes). I want to find all rows where there are any instances of <genericMailer> where the <sendMethod> contains 'Mail'.
I can get all of the values for sendMethod like this:
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol);
however I don't know how to search only for matching values. What's the WHERE clause I need?
Here are some possible ways :
a. Using value() method to extract sendMethod value then check if the value LIKE '%mail%' in WHERE clause :
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol)
WHERE x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') LIKE '%mail%'
b. Using XPath method contains() to check if sendMethod value contains substring "mail" and SQL Server method exist() to filter rows that pass the check :
SELECT a.ApplicationId,
x.XmlCol.value('(moduleContent/data/sendMethod)[1]','VARCHAR(100)') AS SendMethod
FROM Applications a
CROSS APPLY a.AppConfig.nodes('/configuration/modules/genericMailer') x(XmlCol)
WHERE x.XmlCol.exist('moduleContent/data/sendMethod[contains(.,"mail")]') = 1

MyBatis: "Compile-time" mapper params replacement, keeping prepared stmt but making table names configurable?

MyBatis 3.1.1 allows either #{...} for prepared statements params,
or ${...} for every-time replacement.
I am missing something what would allow to parametrize parts of the SQL statement but still keep it prepared statement; i.e. replace during configuration.
How can I do that? Maybe using some SQL fragments?
Update:
I found:
<sql id="userColumns"> id,username,password </sql>
<select id="selectUsers" parameterType="int" resultType="hashmap">
SELECT <include refid="userColumns"/> some_table WHERE id = #{id}
</select>
See http://www.mybatis.org/core/sqlmap-xml.html#sql
This would be it if ${...} could be used inside of it.
I think I found it...
<sql id="userColumns"> id,username,password </sql>
And then
<select id="selectUsers" parameterType="int" resultType="hashmap">
SELECT <include refid="userColumns"/>
FROM some_table
WHERE id = #{id}
</select>
So I will use ${...} in that and that should get me there.
See http://www.mybatis.org/core/sqlmap-xml.html#sql
in my project we have a very simple solution for this case.
We have a String called TABLENAME in the Data Objects.
When we construct the Objects we initialize the tablename.
and in the sqls we have the tablename enquoutet.
in the DataObject:
String TABLENAME;
public String getTABLENAME() {return TABLENAME;}
public void setTABLENAME(String tablename) {this.TABLENAME = TABLENAME;}
in the sqls:
<delete id="simpleDelete" parameterClass="Integer">
delete from ${jdbc.schema}.$TABLENAME$
WHERE ID = #ID#
</delete>
I don't know if this is the best solution, but it works quite well. I am open for better solutions.
Since <sql> doesn't work as needed for this purpose, I filled a feature request.
http://code.google.com/p/mybatis/issues/detail?id=627

How to get a particular attribute from XML element in SQL Server

I have something like the following XML in a column of a table:
<?xml version="1.0" encoding="utf-8"?>
<container>
<param name="paramA" value="valueA" />
<param name="paramB" value="valueB" />
...
</container>
I am trying to get the valueB part out of the XML via TSQL
So far I am getting the right node, but now I can not figure out how to get the attribute.
select xmlCol.query('/container/param[#name="paramB"]') from LogTable
I figure I could just add /#value to the end, but then SQL tells me attributes have to be part of a node. I can find a lot of examples for selecting the child nodes attributes, but nothing on the sibling atributes (if that is the right term).
Any help would be appreciated.
Try using the .value function instead of .query:
SELECT
xmlCol.value('(/container/param[#name="paramB"]/#value)[1]', 'varchar(50)')
FROM
LogTable
The XPath expression could potentially return a list of nodes, therefore you need to add a [1] to that potential list to tell SQL Server to use the first of those entries (and yes - that list is 1-based - not 0-based). As second parameter, you need to specify what type the value should be converted to - just guessing here.
Marc
Depending on the the actual structure of your xml, it may be useful to put a view over it to make it easier to consume using 'regular' sql eg
CREATE VIEW vwLogTable
AS
SELECT
c.p.value('#name', 'varchar(10)') name,
c.p.value('#value', 'varchar(10)') value
FROM
LogTable
CROSS APPLY x.nodes('/container/param') c(p)
GO
-- now you can get all values for paramB as...
SELECT value FROM vwLogTable WHERE name = 'paramB'

SQL Server 2005 Xquery namespaces

I'm trying to get some values out of an Xml Datatype. The data looks like:
<Individual xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Lirria</FirstName>
<LastName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Latimore</LastName>
</Indvidual>
Note the presence of the xmlns in the elements FirstName and LastName - this is added when we create the xml by serializing a c# business object. Anyway it seems that the presence of this namespace in the elements is causing XQuery expressions to fail, such as:
SELECT MyTable.value('(//Individual/LastName)[1]','nvarchar(100)') AS FirstName
This returns null. But when I strip out the namespace from the elements in the xml (e.g. using a Replace T-SQL statement), the above returns a value. However there must be a better way - is there a way of making this query work i.e. without updating the xml first?
Thanks
John Davies
You need to properly name the element you want to select. See Adding Namespaces Using WITH XMLNAMESPACES. Here is an example using your XML:
declare #x xml;
set #x = N'<Individual
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Lirria</FirstName>
<LastName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Latimore</LastName>
</Individual>';
with xmlnamespaces (N'http://nswcc.org.au/BusinessEntities.Crm' as crm)
select #x.value(N'(//Individual/crm:LastName)[1]',N'nvarchar(100)') AS FirstName
The * wildcard will also allow you to select the element without enforcing the explicit namespace. Remus' answer is the way to go, but this may assist others having namespace issues:
select #x.value(N'(//Individual/*:LastName)[1]',N'nvarchar(100)')

Resources