How to fix "ambiguous" where clause in procedure - mysql

I am getting an error:
1052 - Column 'orderId' in where clause is ambiguous
when trying to do CALL GetOrderById(2000)
BEGIN
SELECT
hsorders.*,
hslineitem.partNum, hslineitem.price
FROM
hsorders
JOIN hslineitem ON hslineitem.orderId = hsorders.orderId
WHERE
orderId = orderId;
END
I need to pass an Id and join data from two tables that have the same `orderId``.
What am I doing wrong?

You are getting the error because orderid is in both tables. Hence, the SQL engine does not know which orderid the where clause refers to.
Let me hypothesize that you have a parameter or variable called orderid.
That name conflicts with columns of the same name in the table. That is why I use prefixes. For instance, if orderid is being passed in as a parameter, I would name it in_orderid. If it were a local variable, I might use v_orderid.
Then the code would look like this:
BEGIN
SELECT o.*, li.partNum, li.price
FROM hsorders o JOIN
hslineitem li
ON li.orderId = o.orderId
WHERE o.orderId = v_orderId;
END;
Notice that I also added table aliases so the query is easier to write and to read.

You need to use alias:
BEGIN
SELECT
hsorders.*,
hslineitem.partNum, hslineitem.price
FROM hsorders
JOIN hslineitem ON hslineitem.orderId = hsorders.orderId
WHERE
hsorders.orderId = p_orderId; -- I suggest to rename parameter to avoid
-- name collision
END

ambiguous means use table name or alias name before FIELD
Like hsorders.orderId in where clause.

Use alias while joining tables. It will be helpful to distinguish the columns if they are in both the tables.
When to use alias
BEGIN
SELECT
HO.*,
HL.partNum, HL.price
FROM
hsorders HO
JOIN hslineitem HL
ON HO.orderId = HL.orderId
END
you have already used ON clause to give HO.orderId = HL.orderId
so you do not have to use where clause again

Related

Is there a way to alias a table or database in more than one query

I need to write a sequence of queries where I want to sometimes use a different table or database and don't want to keep writing them over and over again which can lead to a mistake.
SET #NAME = "Fred";
SELECT pid FROM old_table.people WHERE name = #NAME INTO #PID;
INSERT new_table.address SELECT *
FROM old_table.address o WHERE o.pid = #PID;
...
Is there a way todo a global SET or ALIAS so I don't have to repeat the database.table names again and again with the chance of a typo.
ie looking for something like this - this is not real sql
ALIAS old_table.people #OP;
ALIAS new_table.people #NP;
ALIAS old_table.address #OA;
ALIAS new_table.address #NA;
SET #NAME = "Fred";
SELECT pid FROM #OP WHERE name = #NAME INTO #PID;
INSERT #NA SELECT * FROM #OA o WHERE o.pid = #PID;
The closest thing to what you ask for is a VIEW. But I don't see how this would help avoid typos. You'd just be swapping one name for another name. You could make a typo on the view name just as easily.
You definitely cannot use a user-defined variable as a table identifier. When you use a variable in a query, it is as if you used a string value.

How to access columns of subqueries with jooq?

i am having troubles understanding how to access columns from a subquery (MySQL). Here is my code:
Personne personne = Personne.PERSONNE.as("personne");
Evenement evenement = Evenement.EVENEMENT.as("evenement");
Genealogie genealogie = Genealogie.GENEALOGIE.as("genealogie");
Lieu lieu = Lieu.LIEU.as("lieu");
SelectField<?>[] select = { DSL.countDistinct(personne.ID).as("countRs"), lieu.LIBELLE.as("libelleRs"),
lieu.ID.as("idVille") };
Table<?> fromPersonne = evenement.innerJoin(personne).on(personne.ID.eq(evenement.IDPERS))
.innerJoin(genealogie).on(genealogie.ID.eq(personne.IDGEN)).innerJoin(lieu)
.on(lieu.ID.eq(evenement.IDLIEU));
Table<?> fromFamille = evenement.innerJoin(personne).on(personne.IDFAM.eq(evenement.IDFAM))
.innerJoin(genealogie).on(genealogie.ID.eq(personne.IDGEN)).innerJoin(lieu)
.on(lieu.ID.eq(evenement.IDLIEU));
GroupField[] groupBy = { lieu.ID };
Condition condition = //conditionally build, not relevant i think
result = create.select(DSL.asterisk())
.from(create.select(select).from(fromPersonne).where(condition).groupBy(groupBy)
.union(create.select(select).from(fromFamille).where(condition).groupBy(groupBy)))
// i would like something like this but i don't know how: .groupBy(groupBy).fetch();
Basicly what i have is:
SELECT
*
FROM(
(SELECT
countRs, libelleRs, idVille
FROM
fromPersonne
WHERE
-- conditions
GROUP BY lieu.ID)
UNION
(SELECT
countRs, libelleRs, idVille
FROM
fromFamille
WHERE
-- conditions
GROUP BY lieu.ID)
)GROUP BY lieu.ID -- this is where i need help
In a plain MySQL query i would just give an alias to the union and then make a reference to the column i want to group by using the alias but it seems like it does not work like this with JOOQ.
I just need to group the results of the subqueries together but i don't know how to make a reference to the subqueries columns... I am sure i would have to reference my subqueries in objects outside of that "main select" to be able to access the columns or something along those lines but i am lost in all the object types.
You have to assign your derived table to a local variable and dereference columns from it, e.g.
Table<?> t = table(
select(...).from(...).groupBy(...).unionAll(select(...).from(...).groupBy(...))
).as("t");
Field<Integer> tId = t.field(lieu.ID);

Msql getting incompatible with sql_mode=only_full_group_by

i am using mysql version 5.7.13 in linux
When i running the stored procedure i am getting the following error
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause
and contains nonaggregated column 'medigurus.pp.physicianid' which is not
functionally dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
My Procedure is
DELIMITER $$ DROP PROCEDURE IF EXISTS medigurus.getPatientDoctors$$ CREATE
DEFINER=root#localhost PROCEDURE getPatientDoctors(IN patientid INT(10))
BEGIN SELECT pp.physicianid, d.physiciantypeid, p.firstname, p.lastname,
p.dob, p.mobile, p.officephone, p.address1, p.address2, p.city, p.stateid,
p.cid, p.zip,p.speciality, p.about, p.imgid FROM patientphysicians pp INNER
JOIN physician p ON pp.physicianid = p.physicianid INNER JOIN
getpatientdoctorsview AS d ON d.patientid=pp.patientid WHERE pp.patientid
= patientid GROUP BY p.physicianid; END$$ DELIMITER ;
and view is
DELIMITER $$
DROP VIEW IF EXISTS `medigurus`.`getpatientdoctorsview`$$
CREATE ALGORITHM=UNDEFINED
DEFINER=`root`#`localhost` SQL SECURITY DEFINER VIEW `getpatientdoctorsview` AS
select `pp`.`physicianid` AS `physicianid`,`pp`.`patientid` AS
`patientid`,group_concat(`pp`.`physiciantypeid` separator ',') AS
`physiciantypeid`
from `patientphysicians` `pp` group by `pp`.`patientid`$$
DELIMITER ;
How can i fix the error..
A quick solution is to apply an aggregate function to medigurus.pp.physicianid, like this:
select ... min(medigurus.pp.physicianid) as physicianid, ...
The reason for the error message is that medigurus.pp.physicianid is ambiguous as there could be several values per group, and the SQL engine would need to pick one. Without this setting (restriction), it would just take the first one it encounters, but that behaviour is not in line with the SQL standards.
However, by indicating which one of these values you want to return (e.g. via min), you resolve the issue.
Disabling the "only full group by" option
Alternatively, although not advised, you can return to the old behaviour of MySql, by clearing this option:
SET sql_mode = ''
Concerning the procedure and view
As you provided the actual SQL you have in an update of your question, I add here my observation on that:
The view's select groups the result by patientid in order to aggregate the types of the associated physicians. However, you also select the physicianid: this makes little sense, as in general you might have several different physicians associated with one patient, so its value is not singular. You should just omit that field -- or if you really need one physician per patient, you should ask yourself the question: which one, if there is more than one? Here I will suggest to omit it:
select patientid AS patientid,
group_concat(physiciantypeid separator ',') AS physiciantypeid
from patientphysicians
group by patientid
Secondly, the procedure's SQL is also in need of correction. You seem to want to list the details of all the physicians associated with one particular patient. For that purpose it makes no sense to join the view, as the view only returns one record for a patient, and aggregates the physician types into a comma separated list. This is not useful for the SQL you have in the procedure, where you want a separate record for each physician. So remove the view from it, and just return the physician type for each physician.
Apart from that, you should group by the same physicianid as the one you have in the select list. Don't select the physicianid from pp in the select clause and group by the physicianid from p.
Here is the suggested update:
SELECT p.physicianid,
p.physiciantypeid,
p.firstname,
p.lastname,
p.dob,
p.mobile,
p.officephone,
p.address1,
p.address2,
p.city,
p.stateid,
p.cid,
p.zip,
p.speciality,
p.about,
p.imgid
FROM patientphysicians pp
INNER JOIN physician p
ON pp.physicianid = p.physicianid
WHERE pp.patientid = patientid /* the argument */
GROUP BY p.physicianid
Note that it really helps to understand code better when you apply proper indentation.

sql left join for two tables using a CONCAT string as the argument

I need to get a title from table 2, table 2 has title and id column.
Table 1 has some data and three of these columns concatenated together makeup the id that can be found in table 1.
I used CONCAT_WS() function and gave this column an alias name and need to use the Alias for the on argument(At least this is what I understood I needed to do)
I thought this could be a simple left join, yet it is not working for me.
This is my query
SELECT
table_openers.mail,
table_openers.f_name,
table_openers.l_name,
table_openers.Quality,
CONCAT_WS('-',
table_openers.esp,
table_openers.acc,
table_openers.group) as 't1aid',
table_groups.aid,
table_groups.group_name
FROM
lance_mailstats.table_openers
LEFT JOIN
lance_mailstats.table_groups ON table_groups.aid = t1aid;
I get results for mail, f_name, l_name, Quality and t1aid, but the aid and group_name columns of the second table return null.
I feel like you can't use an alias in the ON clause.
Try doing
LEFT JOIN
lance_mailstats.table_groups ON table_groups.aid = CONCAT_WS('-',
table_openers.esp,
table_openers.acc,
table_openers.group);
"You can use the alias in GROUP BY, ORDER BY, or HAVING clauses to refer to the column" (from dev.mysql.com/doc/refman/5.0/en/problems-with-alias.html).
And "The conditional_expr used with ON is any conditional expression of the form that can be used in a WHERE clause" (from dev.mysql.com/doc/refman/5.1/en/join.html).
So as a logical inference you're not allowed to use aliases in ON clauses.
try to use a subquery..
it goes like this.........
ex.
SELECT
tbl1.mail, tbl1.f_name, tbl1.l_name,tbl1.Quality, tbl1.t1aid,table_groups.aid,
table_groups.group_name
FROM
(SELECT
table_openers.mail,
table_openers.f_name,
table_openers.l_name,
table_openers.Quality,
CONCAT_WS('-',
table_openers.esp,
table_openers.acc,
table_openers.group) as 't1aid',
FROM
lance_mailstats.table_openers )tbl1
LEFT JOIN
lance_mailstats.table_groups ON table_groups.aid = tbl1.t1aid;

What does "t" refer to in this SQL?

I have a simple query in my code (shown below) written by my colleague. What does t mean here? Also what is the role of the ; inside the query? I am dead sure that t is not any table, nor any field anywhere in my database. And guess what this query works!!
string query = #"SELECT COUNT(*) FROM (SELECT AttemptNo FROM attempt_exercise
WHERE FK_UId = #uId AND FK_EId = #eId AND Mode = #mode)t;
";
The code follows like this (for any other info if required):
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#uId", uId);
cmd.Parameters.AddWithValue("#eId", eId);
cmd.Parameters.AddWithValue("#mode", mode);
attemptCount = Convert.ToInt32(cmd.ExecuteScalar());
_conn.Close();
return attemptCount;
Your colleague created a query (SELECT COUNT(*)) with a subquery that he named t. This t is just a temporary table name which refers to
SELECT AttemptNo FROM attempt_exercise
WHERE FK_UId = #uId AND FK_EId = #eId AND Mode = #mode
He could have feasibly named it temp to be a bit more explicit. The reason that this becomes like a table is because, in MySQL, a SELECT query returns rows of data which act like a table. So, this inner query gets the AttemptNo, and creates a temporary table t. The outer query then counts this data.
The ; inside the query is to make it a full statement when the string query is called by the program. If this weren't included, the String query wouldn't contain a valid MySQL statement. The final ; is to complete the assignment for the variable.
t is an alias for your subquery, and you need it.
So you could have written:
SELECT COUNT(*) FROM attempt_exercise
WHERE FK_UId = #uId AND FK_EId = #eId AND Mode = #mode;
and that would have been equivalent.
But were you to try to join your subquery to something else, you would have likely seen the need a bit sooner:
SELECT COUNT(*) FROM
(SELECT AttemptNo FROM attempt_exercise
WHERE FK_UId = #uId AND FK_EId = #eId AND Mode = #mode) t
JOIN AttemptStatisticsTableOfSomeSort a
ON t.AttemptNo = a.AttemptNo;
An alias to the subquery. If subquery appears as tablesource in FROM or JOIN clauses - it should have an alias.
It is just an alias for the nested query. The semi colon inside the query is a statement terminator - its part of the ANSI sql standard.
All temporary tables created in this manner (ie. in the FROM clause) MUST have an alias they can be referred to by. In this case, the alias is t. If the coder were more clear in their writing, they wouldn't have omitted the optional AS before it.
The point is, it's just a temporary name and not really important in this scenario, but it required to make it work.
That said, the following is probably more efficient:
SELECT COUNT(DISTINCT AttemptNo) FROM attempt_exercise WHERE FK_UId = #uId AND FK_EId = #eId AND Mode = #mode
The semi colon is a line delimiter for MySql commands. Have a look under the first bullet point on here for more info: http://dev.mysql.com/doc/refman/5.0/en/entering-queries.html