Codeigniter sql binding - mysql

How do I use Codeigniter SQL Binding if there are two target dates?
Is how I did it below correct?
public function getInvestmentForBorrowing($id, $Interest, $Currency, $Loantime, $target_date, $Risk_category)
{
$query = '
select CASE WHEN (a.amount_financed - a.amount_invested - a.amount_withdrawn) < a.amount_per_borrower
THEN round((a.amount_financed - a.amount_invested - a.amount_withdrawn), 2)
ELSE round((a.amount_per_borrower) , 2)
END AS investable_amount, a.*,
c.IBAN as Return_IBAN, c.BIC as Return_BIC,
i.average_rate
from investment a
inner join userinfo c
on a.Owner = c.Owner and
c.UPDATE_DT is null
inner join exchange_rates i
on a.Currency = i.currency_id and
? between i.effective_dt and i.expiration_dt
where a.ORIG_ID = ? and
a.Interest <= ? and
a.Currency = ? and
a.status = 2 and
a.Loantime >= ? and
a.Available >= ? and
a.Risk <= ? and
a.UPDATE_DT is null
having investable_amount > 0';
$query = $this->db->query($query, array($target_date, $id ,$Interest, $Currency, $Loantime ,$target_date ,$Risk_category));
$result = $query->result();
return $result;
}
Write now the question marks just represent the array so I added two $target_date to the array but not sure if thats the right way to do it.

It appears to be ok according to the codeigniter documentation but i say that without regard to your original SQL being correct or not.
Just make sure that the number of ? match the number of values you are providing and they are in the right order.
One way to sanity check it, apart from just running it, is to place the following command right after you perform the query:
echo $this->db->last_query();
And providing it known data, you can cheat and just hard code some dummy values for testing, take the generated SQL and throw that into something like phpmyadmin and run it the generated SQL against the Database and see if it works with the expected results.
Just a side note regarding your variable naming style. I see you are mixing cases i.e. things like $target_date (all lower case) and $Risk_category (First letter uppercase). Just be aware that on a linux based system case does matter and mixing like that can cause errors. It's a good idea to decide on one and stick with it.

Related

How to remove extra apostrophe

I wrote a SQL query to find the desired output for my project. I was working fine with the correct output. But suddenly it started to give error and in the SQL query, there is some additional apoatrophe in. How to resolve it?
I tried to add the query to $this->db->query(); but still no use.
public function getStudentConut($id) {
$this->db->select('students.id')
->from('students')
->join('bp','students.pbp = bp.id','left')
->where(condition 1)
->where(condition 2);
$query1 = $this->db->get_compiled_select();
$this->db->select('students.id')
->from('students')
->join('bp','students.dbp = bp.id','left')
->where(condition 1)
->where(condition 2);
$query2 = $this->db->get_compiled_select();
$this->db->select('COUNT(id) as stud_count')
->from('('.$query1." UNION ALL ".$query2.') X')
->group_by('X.id');
$results = $this->db->get();
return $results->num_rows();
}
It was giving correct count earlier. But without any new changes, it started to give the error.
Now I get error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.id`` WHERE ``bp.some_value`` IS NULL AND ``students.`schoo' at line 2
SELECT COUNT(id) as stud_count FROM (SELECT students.id`` FROM ``students`` LEFT JOIN ``bp`` ON ``students.pbp`` = ``bp.id`` WHERE ``bp..Some other condition.. UNION ALL SELECT students.idFROMstudentsLEFT JOINbpONstudents.dbp=bp.id..some other condition....) X GROUP BYX.id`
I think the issue (at least with the double `) is that CodeIgniter isn't very good with subqueries and such. Basically every time you get the compiled select statement it already has the escape identifiers and then you are putting it in the from statement at the end which will add additional escape identifiers on top of that.
`->from('('.$query1." UNION ALL ".$query2.') X')`
Unfortunately, unlike other methods like set, from doesn't have a 2nd parameter that allows you to set escaping to false (which is what I think you need).
I suggest trying this:
$this->db->_protect_identifiers = FALSE;
$this->db->select('COUNT(id) as stud_count')
->from('('.$query1." UNION ALL ".$query2.') X')
->group_by('X.id');
$results = $this->db->get();
$this->db->_protect_identifiers = TRUE;
and also look in to this: ->where(condition 2); which I'm pretty sure shouldn't compile due to lack of quotes. You probably don't want this escaped so you can do ->where('condition 2', '', false); as per: https://www.codeigniter.com/user_guide/database/query_builder.html#CI_DB_query_builder::where
When all else fails, just know that CodeIgniter has some limitations with "advanced" queries and that maybe you should write it out manually as a string utilizing $this->db->escape_str(...) for escaping user input vars, and $this->db->query(...) to run the SQL.

Symfony Query in controller

$query = $em->query("
SELECT c.id AS id
FROM collectif c, zone z
WHERE
c.zone_id = z.id
AND z.label = '$zone'
ANDc.collectif = '$collectif'
");
$c = $query->fetchAll();
$idc = $c['id'];
I have this query that returns a single line, Symfony shows me an error as what variable id undefined
NB: I know that's don't respect the concept of Symfony [MVC] but it's for a particular reason so if someone can tell me how I can resolve this problem
Thank you
$query->fetchAll() should return numeric array of elements so key id does not exists. You should try $c[0]['id'] to get value.
If you'd rather use the results in the assocative way, you can use fetchAssoc() instead:
$c = $query->fetchAssoc();
$idc = $c['id'];
Here is the documentation for reference:
http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#fetchassoc
I'm just giving an alternate way to do this.

How to put a literal "?" in a Mysql query with bind variables

I have the following query:
select subclasses.id,participants_subclasses.participant_id
from subclasses
left outer join participants_subclasses on
participants_subclasses.participant_id = ?
and subclasses.id = participants_subclasses.subclass_id
where
subclasses.classification_id = ?
and subclasses.showhover
order by subclasses.seq,
IF(LEFT(subclasses.code, 1) = '<',
Extractvalue(subclasses.code, "//texts/text/content"),
subclasses.code)
The above query is processing a table where the code column sometimes has text and sometimes has XML with text inside a tag. The above query works. The side-effect is that a code value cannot start with a "<" which should be acceptable, but the order by would mistake it for XML content. The query below would be more specific and accurate:
select subclasses.id,participants_subclasses.participant_id
from subclasses
left outer join participants_subclasses on
participants_subclasses.participant_id = ?
and subclasses.id = participants_subclasses.subclass_id
where
subclasses.classification_id = ?
and subclasses.showhover
order by subclasses.seq,
IF(LEFT(subclasses.code, 5) = '<?xml',
Extractvalue(subclasses.code, "//texts/text/content"),
subclasses.code)
However this variation checking the XML header in the content fails with a "NameInput Array does not match ?" error in MySQL. It appears that the ? inside <?xml literal is being mistaken for a bind target. And I am passing 2 values to be bound - which again is correct.
So my question is - how do I get the <?xml literal to not be mistaken for a bind value target???
SOLVED
This turns out to be a bug in ADODB interface from phpLens, and NOT in MySQL itself. It exists in current version which is 5.17 for PHP5.

Propel ORM - Custom where clause

I'm trying to match md5(ID) to an id.
SELECT *
FROM `user` u
WHERE
MD5(`user_id`) = '66f041e16a60928b05a7e228a89c3799'
this is ID = 58
I tried something like this. I know I'm close I just don't know what I'm missing
$criteria = new Criteria();
$criteria->addAnd('md5('.User::USER_ID.')', $_REQUEST['fs'], Criteria::CUSTOM);
$user = UserPeer::doSelectOne($criteria);
Any ideas?
First of all, directly using Criteria objects is deprecated not recommended. You should use Active Query classes.
Using these classes, you will be able to write stuff like this :
UserQuery::create()
->where('md5(User.Password) = ?', $_REQUEST['fs'], PDO::PARAM_STR)
->findOne();
You'll notice that I use the PhpName both of the table and the column in the query.
EDIT : For raw conditions, the parameter type has to be specified. You'll find more information on this issue.
After lenghty T&E process I managed to get it done like this
$c = new Criteria();
$c->add(UserPeer::USER_ID, "md5(user.user_id) = \"".$_REQUEST['fs']."\"", Criteria::CUSTOM); // risk of SQL injection!!
$saved_search = UserPeer::doSelectOne($c);
For some reason PropelORM though that $_REQUEST['fs'] was name of the table rather than the value. \"" solved the problem.

Could not format node 'Value' for execution as SQL

I've stumbled upon a very strange LINQ to SQL behaviour / bug, that I just can't understand.
Let's take the following tables as an example: Customers -> Orders -> Details.
Each table is a subtable of the previous table, with a regular Primary-Foreign key relationship (1 to many).
If I execute the follow query:
var q = from c in context.Customers
select (c.Orders.FirstOrDefault() ?? new Order()).Details.Count();
Then I get an exception: Could not format node 'Value' for execution as SQL.
But the following queries do not throw an exception:
var q = from c in context.Customers
select (c.Orders.FirstOrDefault() ?? new Order()).OrderDateTime;
var q = from c in context.Customers
select (new Order()).Details.Count();
If I change my primary query as follows, I don't get an exception:
var q = from r in context.Customers.ToList()
select (c.Orders.FirstOrDefault() ?? new Order()).Details.Count();
Now I could understand that the last query works, because of the following logic:
Since there is no mapping of "new Order()" to SQL (I'm guessing here), I need to work on a local list instead.
But what I can't understand is why do the other two queries work?!?
I could potentially accept working with the "local" version of context.Customers.ToList(), but how to speed up the query?
For instance in the last query example, I'm pretty sure that each select will cause a new SQL query to be executed to retrieve the Orders. Now I could avoid lazy loading by using DataLoadOptions, but then I would be retrieving thousands of Order rows for no reason what so ever (I only need the first row)...
If I could execute the entire query in one SQL statement as I would like (my first query example), then the SQL engine itself would be smart enough to only retrieve one Order row for each Customer...
Is there perhaps a way to rewrite my original query in such a way that it will work as intended and be executed in one swoop by the SQL server?
EDIT:
(longer answer for Arturo)
The queries I provided are purely for example purposes. I know they are pointless in their own right, I just wanted to show a simplistic example.
The reason your example works is because you have avoided using "new Order()" all together. If I slightly modify your query to still use it, then I still get an exception:
var results = from e in (from c in db.Customers
select new { c.CustomerID, FirstOrder = c.Orders.FirstOrDefault() })
select new { e.CustomerID, Count = (e.FirstOrder != null ? e.FirstOrder : new Order()).Details().Count() }
Although this time the exception is slightly different - Could not format node 'ClientQuery' for execution as SQL.
If I use the ?? syntax instead of (x ? y : z) in that query, I get the same exception as I originaly got.
In my real-life query I don't need Count(), I need to select a couple of properties from the last table (which in my previous examples would be Details). Essentially I need to merge values of all the rows in each table. Inorder to give a more hefty example I'll first have to restate my tabels:
Models -> ModelCategoryVariations <- CategoryVariations -> CategoryVariationItems -> ModelModuleCategoryVariationItemAmounts -> ModelModuleCategoryVariationItemAmountValueChanges
The -> sign represents a 1 -> many relationship. Do notice that there is one sign that is the other way round...
My real query would go something like this:
var q = from m in context.Models
from mcv in m.ModelCategoryVariations
... // select some more tables
select new
{
ModelId = m.Id,
ModelName = m.Name,
CategoryVariationName = mcv.CategoryVariation.Name,
..., // values from other tables
Categories = (from cvi in mcv.CategoryVariation.CategoryVariationItems
let mmcvia = cvi.ModelModuleCategoryVariationItemAmounts.SingleOrDefault(mmcvia2 => mmcvia2.ModelModuleId == m.ModelModuleId) ?? new ModelModuleCategoryVariationItemAmount()
select new
{
cvi.Id,
Amount = (mmcvia.ModelModuleCategoryVariationItemAmountValueChanges.FirstOrDefault() ?? new ModelModuleCategoryVariationItemAmountValueChange()).Amount
... // select some more properties
}
}
This query blows up at the line let mmcvia =.
If I recall correctly, by using let mmcvia = new ModelModuleCategoryVariationItemAmount(), the query would blow up at the next ?? operand, which is at Amount =.
If I start the query with from m in context.Models.ToList() then everything works...
Why are you looking into only the individual count without selecting anything related to the customer.
You can do the following.
var results = from e in
(from c in db.Customers
select new { c.CustomerID, FirstOrder = c.Orders.FirstOrDefault() })
select new { e.CustomerID, DetailCount = e.FirstOrder != null ? e.FirstOrder.Details.Count() : 0 };
EDIT:
OK, I think you are over complicating your query.
The problem is that you are using the new WhateverObject() in your query, T-SQL doesnt know anyting about that; T-SQL knows about records in your hard drive, your are throwing something that doesn't exist. Only C# knows about that. DON'T USE new IN YOUR QUERIES OTHER THAN IN THE OUTER MOST SELECT STATEMENT because that is what C# will receive, and C# knows about creating new instances of objects.
Of course is going to work if you use ToList() method, but performance is affected because now you have your application host and sql server working together to give you the results and it might take many calls to your database instead of one.
Try this instead:
Categories = (from cvi in mcv.CategoryVariation.CategoryVariationItems
let mmcvia =
cvi.ModelModuleCategoryVariationItemAmounts.SingleOrDefault(
mmcvia2 => mmcvia2.ModelModuleId == m.ModelModuleId)
select new
{
cvi.Id,
Amount = mmcvia != null ?
(mmcvia.ModelModuleCategoryVariationItemAmountValueChanges.Select(
x => x.Amount).FirstOrDefault() : 0
... // select some more properties
}
Using the Select() method allows you to get the first Amount or its default value. I used "0" as an example only, I dont know what is your default value for Amount.