DISTINCT with as clause - mysql

$query="SELECT a.pk_i_id,a.i_price,b.s_title,c.pk_i_id AS img_id,c.s_extension,d.s_city,d.s_city_area from zl_t_item a, zl_t_item_description b, zl_t_item_resource c, zl_t_item_location d where a.fk_i_category_id=$cat_id and a.pk_i_id=b.fk_i_item_id and a.pk_i_id=c.fk_i_item_id and a.pk_i_id=d.fk_i_item_id ORDER BY a.dt_pub_date DESC";
In this above query i need to add DISTINCT before this c.pk_i_id AS img_id ??
it shows error when i did like below
$query="SELECT a.pk_i_id,a.i_price,b.s_title,DISTINCT c.pk_i_id AS img_id,c.s_extension,d.s_city,d.s_city_area from zl_t_item a, zl_t_item_description b, zl_t_item_resource c, zl_t_item_location d where a.fk_i_category_id=$cat_id and a.pk_i_id=b.fk_i_item_id and a.pk_i_id=c.fk_i_item_id and a.pk_i_id=d.fk_i_item_id ORDER BY a.dt_pub_date DESC";
what is the problem on it?.

It is invalid use of DISTINCT keyword. You can only apply it on a set of columns and not for a specific column skipping other columns

DISTINCT should be applied right after SELECT for a column or set of columns you cannot use DISTINCT between the columns
SELECT DISTINCT c.pk_i_id AS img_id,
a.pk_i_id,a.i_price,b.s_title,c.s_extension,d.s_city,d.s_city_area
from zl_t_item a, zl_t_item_description b, zl_t_item_resource c,
zl_t_item_location d where a.fk_i_category_id=$cat_id
and a.pk_i_id=b.fk_i_item_id and a.pk_i_id=c.fk_i_item_id
and a.pk_i_id=d.fk_i_item_id ORDER BY a.dt_pub_date DESC

In general, using DISTINCT is performance kill.
DISTINCT is actually a filter to remove duplicates.
So, while selecting multiple columns the DISTINCT clause should be applied to the complete set rather than a single column.
Hence you are seeing an error.
The query can be rewritten based on the requirements. If you want filter out duplicates then either you can apply row rank, or group by and having clause to achieve the intended results.

DISTINCT always works on all columns, you might must put it directly after SELECT.
In MySQL there's an easy way to get only one row per img_id, add a GROUP BY img_id
SELECT
a.pk_i_id
,a.i_price
,b.s_title
,c.pk_i_id AS img_id
,c.s_extension
,d.s_city
,d.s_city_area
from
zl_t_item a
,zl_t_item_description b
,zl_t_item_resource c
,zl_t_item_location d
where
a.fk_i_category_id = $cat_id
and a.pk_i_id = b.fk_i_item_id
and a.pk_i_id = c.fk_i_item_id
and a.pk_i_id = d.fk_i_item_id
GROUP BY img_id
ORDER BY
a.dt_pub_date DESC
Of course this is a proprietary MySQL syntax which breaks all the rules of relational dabatabses and will not work with any other RDBMS.

You can have either SELECT DISTINCT <columns> or SELECT <columns> (which actually defaults to SELECT ALL <columns>.) You can't apply DISTINCT to a specific column.
So, the:
SELECT a.pk_i_id ,a.i_price, b.s_title, DISTINCT c.pk_i_id ...
is invalid SQL.

Related

Alternatives to using "having" clause for alias fields

I have this somewhat complex sql query that works ok without the final where clause. I'm looking to filter some records using the column unreviewed_records which is an alias
Problem is that I get an error saying unreviewed_records cannot be found. I found some information saying that alias fields are not permitted to be used in where clauses and I'm not sure what's the best way to fix this. Considered using a computed column but I'm not sure how that works yet and I'm hoping there's an easier fix to the query.
Also I find that switching to using the "having" clause work for aliases, but I'll only resort to this if there's no better alternative, to avoid the performance hit.
Any pointers would be helpful :)
select
r_alias.serv_id, r_alias.node_id,
SUM(g_alias.total_records)- SUM(r_alias.reviewed_records) AS unreviewed_records,
SUM(r_alias.reviewed_records) AS reviewed_records,
SUM(g_alias.total_records) AS total_records,
FROM (
SELECT prs.serv_id,
prs.node_id,
SUM(prs.reviewed_records) AS reviewed_records,
FROM p_rev_server prs
WHERE
prs.area_id = 3
AND prs.subId = 3
AND prs.sId = 12
GROUP BY prs.serv_id, prs.node_id, prs.domain_name
) r_alias
INNER JOIN (SELECT
serv_id,
node_id,
SUM(pgs.total_records) AS total_records,
FROM p_gen_serve pgs
WHERE pgs.area_id = 3
AND pgs.subId = 3
AND pgs.sId = 12
AND pgs.total_records > 0
GROUP BY pgs.serv_id, pgs.node_id, pgs.domain_name
) g_alias
ON g_alias.serv_id = r_alias.serv_id AND g_alias.node_id = r_alias.node_id
LEFT JOIN p_cust_columns cust_cols
ON cust_cols.node_id = r_alias.node_id AND cust_cols.serv_id = r_alias.serv_id
where (((NOT (unreviewed_records IS NULL)) AND (unreviewed_records = 5)))
group by r_alias.serv_id, r_alias.node_id
order by g_alias.node_id ASC
limit 25
The reason aliases are not allowed in a WHERE clause is that the expressions in the SELECT list are not evaluated until after the rows are filtered by the WHERE clause. So it's a chicken-and-egg problem.
The easiest and most common alternative is a derived table:
SELECT a, b, c
FROM (
SELECT a, b, a+b AS c
FROM mytable
WHERE b = 1234
) AS t
WHERE c = 42;
This example shows that you can put some filtering conditions inside the derived table subquery, so you can at least reduce the result set partially, before the result of the subquery is turned into a temporary table.
Then in the outer query, you can reference a column that was derived from an expression in the select-list of the subquery. In this example, it's the c column.
The CTE approach is basically the same, it creates a temporary table to store the result of the inner query (the CTE), and then you can apply conditions to that in the outer query.
WITH t AS (
SELECT a, b, a+b AS c
FROM mytable
WHERE b = 1234
)
SELECT a, b, c
FROM t
WHERE c = 42;
The CTE solution is not better than the derived-table approach, unless you need to reference the CTE multiple times in the outer query, i.e. doing a self-join.
Yeah, you are kind of SOL, WHERE can't know what an alias will be. So, frankly, a CTE, common table expression, is probably your best bet here. It should work, though not all RDBMS really support them (MySQL for example only in version 8).

MySQL - inner join on select query error 1052

Im trying to do a select query on a table along with an inner join afterwards to link data from the owner to the cats
the ownercat is using a foreign key on the id linking to the ownerinfo id
USE CATTERY;
SELECT
OWNERINFO.ID, OWNERINFO.First_Name, OWNERINFO.Last_Name, OWNERINFO.Phone, OWNERINFO.AddrL1, OWNERINFO.AddrL2, OWNERINFO.AddrL3, OWNERINFO.PostCode,
GROUP_CONCAT(DISTINCT OWNERCAT.Chip_ID)
FROM OWNERINFO
INNER JOIN OWNERCAT ON OWNERINFO.ID = OWNERCAT.ID
WHERE ID = 1;
I get returned the following error:
Error Code: 1052. Column 'ID' in where clause is ambiguous 0.0014 sec
removing the concat distinct statement still produces the same error, im not sure how to get around this issue
You need to define from which table the ID on WHERE-clause come from (you can use aliases). Secondly, as you are using GROUP_CONCAT, you should have GROUP BY in the query:
SELECT
oi.ID,
oi.First_Name,
oi.Last_Name,
oi.Phone,
oi.AddrL1,
oi.AddrL2,
oi.AddrL3,
oi.PostCode,
GROUP_CONCAT(DISTINCT oc.Chip_ID)
FROM OWNERINFO oi
INNER JOIN OWNERCAT oc ON oc.ID=oi.ID
WHERE oi.ID = 1
GROUP BY oi.ID
The problem is in the WHERE clause: ID is ambiguous, because that column is available in both tables.
You may think that, since you are joining the tables on ID, the database is able to tell that it has the same value, but that's not actually the case.
So just qualify the column in the WHERE clause, ie change this:
WHERE ID = 1
To either:
WHERE OWNERINFO.ID = 1
Or the equivalent:
WHERE OWNERCAT.ID = 1
Also please note that your query uses GROUP_CONCAT(), which is an aggregate function. This implies that you need a GROUP BY clause, that should list all non-aggregated column (ie all columns other than the one that is within GROUP_CONCAT()).

Not able to use GROUP BY with join query in MySQL

I need to execute the below Query but not able to do it.
SELECT *
FROM (SELECT *
FROM rider_status
order by created_date DESC) as riderActice
INNER JOIN rider_user ON rider_userId = rider_user.id
Where rider_user.isActive = 1
AND online_status = 1
GROUP by rider_userId
Error: "#1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'riderActice.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by"
I have read some blogs and found the below solution.
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
But Yet executing above solution I am getting another issue which is
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
Please let me know if I am missing something or doing it wrong
You can not select all(* means all) columns from a table and do a group by one column. That is why you are getting an error. If the column is in the select clause and it is not a part of aggregate function then it has to be in a group by clause.
Also, use the aliases you have created riderActice when joining two tables(subquery and a table).
Here is a small demo demonstrating first part of my answer.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b10244e667e59bffb146170014dc69ba
If you want to select all columns then do it like this:
SELECT riderActice.rider_userId
, riderActice.created_date
, rider_user.id
, rider_user.isActive
, rider_user.online_status
FROM rider_status as riderActice
INNER JOIN rider_user ON riderActice.rider_userId = rider_user.id
WHERE rider_user.isActive = 1
AND rider_user.online_status = 1
GROUP BY riderActice.rider_userId
, riderActice.created_date
, rider_user.id
, rider_user.isActive
, rider_user.online_status
If you do not want to group by every column then explain to us what is it that you need, but this is the answer to your question.

MYSQL The used SELECT statements have a different number of columns

I need to combined 2 tables with the same ids in it but i can't
SELECT stat.user_id, user.username,
SUM(stat.vertrag) AS vertrag,
SUM(stat.zubehoer) AS zubehoer,
SUM(stat.privat) AS privat,
SUM(stat.service) AS service,
SUM(stat.bs_vertrag) AS bus
FROM statistics stat
join users user on stat.user_id = user.uid
WHERE stat.user_id != '0' AND stat.datum LIKE '%$month%'
GROUP BY stat.user_id
UNION
SELECT bew.user_id, stat.user_id, user.username,
SUM(case when bew.log = 'inv_imei'
THEN
1
ELSE
0
END) AS inv
FROM user_bewegungen bew
JOIN users user ON user.uid = bew.user_id
JOIN statistics stat ON bew.user_id = stat.user_id
WHERE bew.date LIKE '%$month%'
GROUP BY bew.user_id
ORDER BY vertrag DESC
I am dont know how to go now.....
The first select is perfect, and works. now i have add a union because i need to add the row "log". Id's are also in it but i become the error
The used SELECT statements have a different number of columns
Can anyone help?
Each select statement needs to have the same number of columns. Your first one has 7:
SELECT
stat.user_id,
user.username,
SUM(stat.vertrag) AS vertrag,
SUM(stat.zubehoer) AS zubehoer,
SUM(stat.privat) AS privat,
SUM(stat.service) AS service,
SUM(stat.bs_vertrag) AS bus
Your second one has 4:
SELECT
bew.user_id,
stat.user_id,
user.username,
SUM(case when bew.log = 'inv_imei' THEN 1 ELSE 0 END) AS inv
You can select NULL in the second SELECT for those columns that aren't in the first one.
Make the two operands of the UNION isomorphic. Rename columns and/or create NULL-valued dummy columns as necessary to give them the same shape. FOR EXAMPLE, if we wanted to form the UNION of:
SELECT a, b, c
FROM table1
and:
SELECT d, e
FROM table2
we would logically pair those columns that are of the same types (in this case, let's assume that a and e are of the same type, and that b and d are of the same type) and add an extra NULL-valued column as the third projected attribute of the right-hand SELECT, as follows:
SELECT b, a, c
FROM table1
UNION
SELECT d AS b, e AS a, NULL as c
FROM table2
If such an approach seems confusing, you can use table views to simplify the expression. In the preceding example, you could have asserted a view atop table2:
CREATE VIEW t2view( b, a, c )
AS
SELECT d, e, NULL
FROM table2
and then formulated your UNION as:
SELECT b, a, c
FROM table1
UNION
SELECT *
FROM t2view
In UNION, the field numbers should be the same. Use like this:
SELECT stat.user_id, 0, user.username, ....
SELECT bew.user_id, stat.user_id, user.username, ...
or use something else, what you know, that is a missing field there.
The data types should be the same also.
You are using MySQL Union.
UNION is used to combine the result from multiple SELECT statements into a single result set.
The column names from the first SELECT statement are used as the column names for the results returned. Selected columns listed in corresponding positions of each SELECT statement should have the same data type. (For example, the first column selected by the first statement should have the same type as the first column selected by the other statements.)
Reference: MySQL Union
Your first select statement has 7 columns and second statement has 4.
You should have same number of column and also in same order in both statement.
otherwise it shows error or wrong data.
you can see this example
there are two queries both queries have the same number of columns.
column name can be different.
select 'row1' as column1,'row2' as column2
union
select 'row3' as column11,'row4' as column222
if you change columns count, it means in first query you are selecting 2 columns and in second query you are using 3 columns then it will through an error (The used SELECT statements have a different number of columns).
select 'row1' as column1,'row2' as column2
union
select 'row3' as column11,'row4' as column222 ,'rr' as t ;
run both queries you will see differnce.

Finding even values in a table MySQL

In MySQL, i have a table with a column full of positive integers and i want to filter out all the odd integers. It seems like there is nothing in the MySQL documentation. I tried the following query.
select kapsule.owner_name,
kapsule.owner_domain,
count(xform_action)
from kapsule, rec_xform
where rec_xform.g_conf_id=kapsule.g_conf_id
and (count(xform_action))%2=0
group by kapsule.owner_name;
I want to keep only those values where count(xform_action) is even. The table looks like this.
To filter out resultset after GROUP BY you need to use HAVING clause.
WHERE clause is used to filter source rows before GROUP BY occurs.
Try
SELECT k.owner_name,
k.owner_domain,
COUNT(x.xform_action) cnt -- < you probably meant to use SUM() instead of COUNT() here
FROM kapsule k JOIN rec_xform x -- < use JOIN notation for clarity
ON x.g_conf_id = k.g_conf_id
GROUP BY k.owner_name
HAVING cnt % 2 = 0
You probably meant to use SUM() (sums values of a column of all rows in a group) aggregate instead of COUNT() (returns number of rows in a group)
Here is SQLFiddle demo (for both SUM() and COUNT())
For aggregate functions like COUNT(*) using GROUP BY you need to use HAVING clause
select kapsule.owner_name, kapsule.owner_domain,
count(xform_action) from kapsule, rec_xform
where rec_xform.g_conf_id=kapsule.g_conf_id and
group by kapsule.owner_name, kapsule.owner_domain
HAVING (count(xform_action))%2=0
or you could use alias (i.e. AS) like:
select kapsule.owner_name, kapsule.owner_domain,
count(xform_action) count_form from kapsule, rec_xform
where rec_xform.g_conf_id=kapsule.g_conf_id and
group by kapsule.owner_name, kapsule.owner_domain
HAVING count_form%2=0
And you could use JOIN as more efficient than the old one of joining tables. And by the way
if you have GROUP BY the fields before the aggregate function should be in GROUP BY like:
select kapsule.owner_name, kapsule.owner_domain,
count(xform_action) count_form from kapsule A
INNER JOIN rec_xform B
ON A.g_conf_id=B.g_conf_id and
GROUP BY by A.owner_name, A.owner_domain
HAVING count_form%2=0
See examples here