PHP Postgres JOIN with LIKE query failing - php

I am trying to figure out a query with postgres, and Im not sure its cause I am used to mysql and postgres doesn't have a LIKE query. Or what my deal is. Either way I know its not working and I have no idea why not. Can someone help me out point me in the right direction? I mean ultimately I am trying to make a Zend Database version of this query but, this is the core query I am working with to try and make before I attempt to use zend db class to build it.
SELECT
org.orgid,
org.roleid,
users.userid,
users.email,
users.first_name,
users.last_name,
users.contact_id,
users.state,
users.ts,
users.altemail,
users.unboundid,
users.blocked
FROM mapping AS org
INNER JOIN my_users AS users ON org.userid = users.userid
WHERE (org.orgid = 'generated-id')
AND (org.roleid LIKE 'partner-%');
ERROR: operator does not exist: roles ~~ unknown
LINE 17: AND (org.roleid LIKE 'partner-%');
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.

Change
AND (org.roleid LIKE 'partner-%');
to
AND (org.roleid::text LIKE 'partner-%');
Evidently you are working on the textual representation of the roleid. this seems like it should work fine except your data types do not match. By casting to text you ensure that they do.
Note that one important ramification is that indexes on roleid will be useless for such a comparison because they operation is not directly compatible with the type. This shouldn't be a problem since orgid can still use an index but it is something to think about. If you want to fix that, you may want to use table methods or other functions to break this down for you. You can then index function output.
For example you could use a table method as like so:
CREATE FUNCTION is_partner(mapping) returns bool language sql immutable as $$
SELECT $1.roleid::text LIKE 'partner-%';
$$;
You could then index it with:
CREATE INDEX mapping_is_partner_idx ON mapping (is_partner(mapping));
You could then change that join condition from
AND (org.roleid LIKE 'partner-%');
to
AND org.is_partner;
Note in this case, org is necessary and cannot be added implicitly since it changes org.is_oartner to is_partner(org) using class.method notation.
Hope this helps.

Related

How it works a mysql query with alias?

I'm new in this comunnity and I need to work with a query that get data from a mysql database, I have this query, but I need to add a new table and I don't understand why the query has a alias, I don't know how it works, someone can help me?
This is my query:
SELECT ins.matricula, pe.nombres, pe.appaterno, pe.apmaterno, co.calleynum, co.colonia, co.municipio, co.telfijo, co.telcelular, pe.fechanac, pe.sexo, co.email, pe.institucion, tu.tnombres, tu.tappaterno, tu.tapmaterno, tu.direccion, tu.telefono, ins.fechains, ins.niveledu, ins.fechaini, ins.horario
FROM Inscripciones ins
LEFT JOIN Perfiles pe
ON pe.idperfil=ins.idperfil
LEFT JOIN Contactos co
ON co.idperfil = pe.idperfil
LEFT JOIN Tutores tu
ON tu.matricula = ins.matricula
WHERE pe.idperfil='$var'
I have read the mysql docs but I can't understand how it works.
In MySQL, an "alias" can be declared to simplify the query later.
Typically this is denoted with the "AS" operator, but can also be declared without "AS" - as in your example.
In your example:
SELECT ins.matricula, {...}
FROM Inscripciones ins {...}
The ins is set as an alias for the "Inscripciones" table.
This allows you to use ins throughout the query rather than typing out "Inscripciones." This can be seen in the SELECT statement.
Something to keep in mind - aliases in SQL can be declared after they're first used. This is the case in your example, where SELECT gets ins.matricula before you've actually declared ins as the alias for Inscripciones.
Sometimes this seems counter intuitive, but I promise it will make sense if you experiment with it a bit.
I find it less ambiguous to include the "AS" - which might help it make more sense as you're reading/writing the SQL query.
ex: ... FROM Inscripciones AS ins
To be clear, the use of the alias doesn't change the outcome of your query, but helps you write cleaner queries because you don't have to re-write the tablename every time you want to use it.
A SQL Alias is just what the name says, an alias. It's simply another name (a shorter name) for your table name.
So in your example the table name is Inscripciones, and in this line FROM Inscripciones ins you're saying "ins" is an alias to Inscripciones. Its just a way to make the query smaller/simpler. An alias is like a nickname (i.e. an alias for Michael is "Mike")
Aliases are normally set with "AS" like this:
SELECT * FROM Users AS u
but can be shortened like this:
SELECT * FROM Users u
Tables don't have to have aliases, unless you want to use the same table more than once, but it can make things shorter to type if the tables have columns named the same.
Instead of having to write
SELECT myfulltable1name.id, myfulltable2name.id
You can write
SELECT t1.id, t2.id
(If you've aliased your tables as t1 and t2)
Here's an example query where we use the same table more than once and need an alias to separate them:
SELECT
workAddresses.City as WorkCity,
homeAddresses.City as HomeCity
FROM
Addresses workAddresses
INNER JOIN
Addresses homeAddresses
ON
workAddresses.UserID = homeAddresses.UserID
WHERE
workAddresses.type = 'work' AND
homeAddresses.type = 'home'
Here the Addresses table stores work and home addresses for our users, with a type column to differentiate them. We want a result where tThe work and home address is on the same row, so we have to join the addresses table in twice, we give them sensible aliases so we can tell which is which, and we use where clause to make sure our workAddress table alias only refers to those records with type 'work'
You'll notice I also put an alias on the column names selected, so you can know which is the work City and which is the home city
Sometimes you MUST use an alias, like if you make a subquery, then the result must be aliased in order to be usable:
SELECT
workAddresses.City as WorkCity,
homeAddresses.City as HomeCity
FROM
(SELECT * FROM Addresses WHERE type ='work') workAddresses
INNER JOIN
(SELECT * FROM Addresses WHERE type ='home') homeAddresses
ON
workAddresses.UserID = homeAddresses.UserID
Here we got information from the addresses tables by subquery, and the bracketed sql statements must have aliases
Aliases are always declared at the first point that the object being aliased is brought into the query. For tables, this is in the FROM section, for columns this is in the SELECT section
It might help you to consider it as if the database actually does the from section first, connecting all the tables together, then it does the where, to filter the rows, finally it does the select, to pull just the columns you want. C# has a built in query language called LINQ, that presents things in this more logical way, FROM table WHERE something = something SELECT somecolumns - makes it easier to say "things are aliased when they are first introduced, like variable naming". SQL is what it is, has been for years. You get used to it
Alias in MySQL query is like a temporary short name for your table. They are not necessarily to be used but save your time when you need to create a complex queries. They are mainly used in queries when you use try to fetch data more than 1 table in single query using JOINS.
Suppose you have a table name 'employee_records'. Then trying to fetch the fields for table would look employee_records.id and so on. If you use alias for table name say 'e', then selecting fields will become e.id.
Hope that will make your point clear.
For more information in simple words read about SQL aliases over here - https://www.w3schools.com/sql/sql_alias.asp

How to use SUBSTRING() in mysql WHERE query in php

I have a string in a database field (called term6eyfs) that is made up of numbers -> 555555.
I want to count how many of them have a particular number in a particular position.
I have tried the following code, but I'm met with a Boolean given... error
$pos=2;
$analyse_ot="SELECT COUNT(*) AS ot_count FROM base, users
WHERE base.base_id=$base
AND
users.base_id=$base
AND
users.SUBSTRING(term6eyfs,$pos,1)='4'";
$result_ot=mysqli_query($con,$analyse_ot);
$row_ot = mysqli_fetch_assoc($result_ot);//this is where I get the error
$total_ot= $row_ot['ot_count'];
$otper=($total_ot/$total)*100;
I'm guessing that they way I have constructed my query (particularly the final line) isn't correct, but why?
Based on the additional details you gave in the comment I'd say that is what you are looking for:
SELECT COUNT(*) AS ot_count
FROM base, users
WHERE base.base_id=$base
AND users.base_id=$base
AND SUBSTRING(users.term6eyfs,$pos,1)='4'";
(this assumes that "term6eyfs" is the name of a column in the users table)
The context of this query is unclear. But in general it does make sense to use "parameter binding" to inject php variable values into a query string. You want to read about that, it enhances security and robustness.
Also reconsider if you really want to use the , operator to join those two tables. That operator is extremely slow, usually a LEFT JOIN delivery a much better performance.
Change users.substring() to substring
$analyse_ot="SELECT COUNT(*) AS ot_count FROM base, users
WHERE base.base_id=$base
AND
users.base_id=$base
AND
SUBSTRING(term6eyfs,$pos,1)='4'";

Doctrine 2.1: Getting and assigning COUNT(t.id) from a subquery?

I have two entities in Doctrine 2.1: Category and Site each category has many sites and each site has a parent category.
I would like to make a single update query (in DQL) which will update a field called count of the Category entity with the number of related sites.
So in SQL I would do something like this:
UPDATE categories c SET c.count = (SELECT COUNT(s.id) FROM sites s WHERE s.category_id = c.id);
This would work beautifuly, in DQL it might something like this:
UPDATE PackageNameBundle:Category c SET c.count = (SELECT COUNT(s.id) FROM PackageNameBundle:Site s WHERE s.category = c)
Such attempt raises [Syntax Error] line 0, col 61: Error: Expected Literal, got 'SELECT'.
Subqueries DO work in DQL, but the problem here (as far as I see it) is that Doctrine cannot assign the returned value from the subquery, to the c.count. This is understandable since I might fetch more than 1 field in the subquery and even more than one row. It magicaly works in MySQL since it sees one row, one field and for convenience returns a single integer value. Doctrine on the other hand has to be object oriented and has to work with different engines where such convertions might not be supported.
Finally, my question is:
What is the best way to do this in Doctrine, should I go with Native SQL or it can be done with DQL and how?
Thanks in advance!
EDIT: I just found this quote in the DQL Docs:
References to related entities are only possible in the WHERE clause and using sub-selects.
So, I guess assigning anything but a scalar value is impossible?
The main question remains though..
You can use native sql queries in Doctrine also, for that kind of specific queries. DQL is powerful in its own way, but it's also limited due to performance constraints. Using native sql queries and mapping the results will achieve the same thing, and there is no disadvantage in doing that.
The documentation explains it in detail.

Behavior Difference between find('count') and find('all') cakePHP

I am having an odd problem in CakePHP where
$this->something->find('count');
works perfectly, yet
$this->something->find('all');
returns nothing (not even an empty array, any errors, or anything).
edit: turns out I am getting an sql error: "SQL Error: 1054: Unknown column" - for a column that does indeed exist. (users.display_name in the sql query below):
SELECT item.id, item.name, item.description, item.user_id, users.display_name FROM item LEFT JOIN users ON (item.user_id = users.id);
I also tried using findAllBy as well as paginate (paginate is actually what I am trying to do - although from what I've gathered, paginate and find('all') are pretty similar in functionality).
The odd thing is that find('all') works everywhere else - it's just in this specific controller that it is acting odd. I am not getting any errors, simply an empty result.
I'm thinking that I may be overlooking something quite simple, but any help is appreciated. Thanks!
So, as per our discussion, the problem you're having is with the virtual fields. Have a look at the documentation, more specifically at the virtual fields and model aliases and limitation of virtualFields sections.
From your description above, it looks like you have a join specified your virtual field which would be causing the error you're seeing because it'll add the JOIN before the FROM. If you insist on using the virtual field, I'd suggest you rewrite it to use a subquery. Make sure your subquery only returns 1 column.
Example: (http://web-development-blog.co.uk/2011/03/08/cakephp-virtual-field-count-another-modeltable/)
public $virtualFields = array(
'count' => 'SELECT COUNT(*) FROM stacks Stack'
);
Alternatively, you can use the Model::beforeFind to bind the necessary models (if necessary) and change the query parameters.
If you can't figure it out, please post your model and I'll help you.
The specific problem you're having with the difference in behaviour is that find('count') will run a basic COUNT(*) query on your database to determine the number of rows.
find('all'), however, runs a different query, and if the SQL you've provided is what it's trying to use, it's invalid:
SELECT item.id, item.name, item.description, item.user_id, users.display_name LEFT JOIN users ON (item.user_id = users.id);
There's no FROM declaration (SELECT from what, exactly?), and if you've not customised your Item or User or App models, (setting $useTable = false maybe?) you're dealing with an unusual error.
One thing to look out for, if these models work fine in other controllers, is any alteration to the properties of each model in each controller. They won't act differently on a per-controller basis unless you have code in each controller that tells it to.

Is there a way to make a table name dynamic in a query?

I am trying to create a Class-Inheritance design for products.
There is the base table that contains all the common fields. Then for each product type there is a separate table containing the fields that are for that product type only
So in order to get all the data for a product I need to JOIN the base table with whatever table that correlates to the product_type listed in the base table. Is there a way to make this query join on the table dynamically?
Here is a query to try to illustrate what I am trying to do:
SELECT * FROM product_base b
INNER JOIN <value of b.product_type> t
ON b.product_base_id = t.product_base_id
WHERE b.product_base_id = :base_id
Is there a way to do this?
No, there's no way to do this. The table name must be known at the time of parsing the query, so the parser can tell if the table exists, and that it contains the columns you reference. Also the optimizer needs to know the table and its indexes, so it can come up with a plan of what indexes to use.
What you're asking for is for the table to be determined during execution, based on data found row-by-row. There's no way for the RDBMS to know at parse-time that all the data values correspond to real tables.
There's no reason you would do this to implement Class Table Inheritance. CTI supports true references between tables.
You're instead describing the antipattern of Polymorphic Associations.
Make 2 queries:
First select < value of b.product_type > and then use it in the second one (the one that you have, but replace < value of b.product_type > with the result from the first one).
No. There would be little point even if it were possible, as the query optimiser would not be able to make a plan without knowing anything about the right- hand side of the join.
You need to construct the query using concatenation or similar, but make sure that you only use a valid table name to avoid injection attacks.
You can create a procedure that takes the table name as an argument and constructs a dynamic-SQL query. But it's probably easier to do this in your server-side code (PHP). But rather than make it a variable (and as suggested vulnerable to injection attacks), create separate classes for the different join combinations. Use another class (like a dispatcher) to determine the correct class to instantiate.

Categories