MySQL display only unique rows - mysql

I have a query:
SELECT DISTINCT ZDJECIA.Id_ogloszenia, OGLOSZENIA.Opis, TYP_NADWOZIA.Nazwa_nadwozie,
WOJEWODZTWA.Nazwa_wojewodztwo, OGLOSZENIA.Miasto, OGLOSZENIA.Rocznik,
OGLOSZENIA.Cena, OGLOSZENIA.id_model, MARKI.Nazwa_marka, MODELE.Nazwa_model,
ZDJECIA.Zdjecie, SILNIK.Nazwa_silnik, KOLORY.Nazwa_kolor, OGLOSZENIA.Moc,
OGLOSZENIA.Pojemnosc
FROM OGLOSZENIA, MARKI, MODELE, ZDJECIA, WOJEWODZTWA, TYP_NADWOZIA, SILNIK, KOLORY
WHERE OGLOSZENIA.Id_koloru = KOLORY.Id_koloru
AND OGLOSZENIA.Id_silnik = SILNIK.Id_silnik
AND OGLOSZENIA.Id_kategoria = TYP_NADWOZIA.Id_kategoria
AND MODELE.Id_marka = MARKI.Id_marka
AND OGLOSZENIA.Id_model = MODELE.Id_model
AND OGLOSZENIA.Id_ogloszenia = ZDJECIA.Id_ogloszenia
AND OGLOSZENIA.Id_wojewodztwa = WOJEWODZTWA.Id_wojewodztwa
ORDER BY OGLOSZENIA.Id_ogloszenia DESC LIMIT 3;
In table 'OGLOSZENIA' I have one record.
In Table 'ZDJECIA' I have two records.
'ZDJECIA':
Id | Id_ogloszenia | Zdjecie
1 1 test1.jpg
2 1 test2.jpg
My query displays two rows, one with test1.jpg, second with test2.jpg.
I don't know how display only one row with first row in 'ZDJECIA' (only test1.jpg);

You need to specify more carefully what you want.
Judging from your comment that 'there will be many rows in table Ogloszenia but you need to use one record from Zdjecia' (paraphrasing), it sounds as if you want one row from Zdjecia for each ID in Ogloszenia. If that's the case, you need to decide which row is appropriate. For example, it might be the row with the smallest ID value.
Rewriting your original query with JOIN notation (as recommended in a comment), I get:
SELECT DISTINCT Z.Id_ogloszenia, O.Opis, T.Nazwa_nadwozie,
W.Nazwa_wojewodztwo, O.Miasto, O.Rocznik,
O.Cena, O.id_model, I.Nazwa_marka, E.Nazwa_model,
Z.Zdjecie, S.Nazwa_silnik, K.Nazwa_kolor, O.Moc,
O.Pojemnosc
FROM OGLOSZENIA AS O
JOIN MODELE AS E ON O.Id_model = E.Id_model
JOIN MARKI AS I ON E.Id_marka = I.Id_marka
JOIN ZDJECIA AS Z ON O.Id_ogloszenia = Z.Id_ogloszenia
JOIN WOJEWODZTWA AS W ON O.Id_wojewodztwa = W.Id_wojewodztwa
JOIN TYP_NADWOZIA AS T ON O.Id_kategoria = T.Id_kategoria
JOIN SILNIK AS S ON O.Id_silnik = S.Id_silnik
JOIN KOLORY AS K ON O.Id_koloru = K.Id_koloru
ORDER BY O.Id_ogloszenia DESC LIMIT 3;
Now we need to put a modestly complex sub-query in place on the Zdjecia table.
The sub-query needs to find the Z.Zdjecie value corresponding to the minimum ID value for each ID_ogloszenia:
SELECT Z1.ID_ogloszenia, Z1.Zdjecie
FROM Zdjecia AS Z1
JOIN (SELECT Z2.ID_ogloszenia, MIN(Z2.ID) AS ID
FROM Zdjecia AS Z2
GROUP BY Z2.ID_ogloszenia
) AS Z2
So we embed that into the main query:
SELECT DISTINCT Z.Id_ogloszenia, O.Opis, T.Nazwa_nadwozie,
W.Nazwa_wojewodztwo, O.Miasto, O.Rocznik,
O.Cena, O.id_model, I.Nazwa_marka, E.Nazwa_model,
Z.Zdjecie, S.Nazwa_silnik, K.Nazwa_kolor, O.Moc,
O.Pojemnosc
FROM OGLOSZENIA AS O
JOIN MODELE AS E ON O.Id_model = E.Id_model
JOIN MARKI AS I ON E.Id_marka = I.Id_marka
JOIN (SELECT Z1.ID_ogloszenia, Z1.Zdjecie
FROM Zdjecia AS Z1
JOIN (SELECT Z2.ID_ogloszenia, MIN(Z2.ID) AS ID
FROM Zdjecia AS Z2
GROUP BY Z2.ID_ogloszenia
) AS Z2
) AS Z ON O.Id_ogloszenia = Z.Id_ogloszenia
JOIN WOJEWODZTWA AS W ON O.Id_wojewodztwa = W.Id_wojewodztwa
JOIN TYP_NADWOZIA AS T ON O.Id_kategoria = T.Id_kategoria
JOIN SILNIK AS S ON O.Id_silnik = S.Id_silnik
JOIN KOLORY AS K ON O.Id_koloru = K.Id_koloru
ORDER BY O.Id_ogloszenia DESC LIMIT 3;
Since the sub-query returns just one row per ID_ogloszenia, there will only be one row returned in the main query. If you don't like the MIN(), you can use MAX() or any other single-valued aggregate.

Did not know which one is the ID .. took Opis as ID ;)
SELECT OGLOSZENIA.Opis as ID, ZDJECIA.Id_ogloszenia, ZDJECIA.Zdjecie
FROM OGLOSZENIA
LEFT JOIN ZDJECIA ON OGLOSZENIA.Id_ogloszenia = ZDJECIA.Id_ogloszenia
Group by OGLOSZENIA.Id_ogloszenia
ORDER BY OGLOSZENIA.Id_ogloszenia DESC LIMIT 3;

Use LIMIT 1 instead of 3 at the end of your query. This tells MySQL to return only the first result.
Here is a documentation that explains more about LIMIT: http://dev.mysql.com/doc/refman/5.6/en/limit-optimization.html
Good luck.

You need to make your query more precise if you only want 1 row to be returned. This means adding another WHERE condition in your query.
Right now, the query that you're giving it is returning 2 rows. It's returning exactly what you're asking for.
If you set LIMIT = 1, there's no guarantee whether or not you will get test1.jpg returned or test2.jpg returned, since your ORDER BY clause is ordering based on Id_ogloszenia, which is the same for both rows.

Related

Getting wrong COUNT values in query

I am using the following query to get COUNT items from rows from the same table in LEFT JOIN.
This is the query:
SELECT
pac.id_sat as id_sat,
pac.nombre_contacto as nombre_contacto,
pac.centro_contacto as centro_contacto,
pac.tel_contacto as tel_contacto,
pac.horario_contacto as horario_contacto,
pac.email_contacto as email_contacto,
pac.num_factura as num_factura,
pac.fecha_factura as fecha_factura,
eq.nombre_equipo as modelo_equipo,
pac.num_serie as num_serie,
pac.tipo_incidencia as tipo_incidencia,
pac.cod_sat as cod_sat,
pac.estado as estado,
pac.clinica as clinica,
pac.fecha_sat as fecha_sat,
COUNT(medfotos.id_media_sat) as num_fotos,
COUNT(medvideos.id_media_sat) as num_videos
FROM tb_sat pac
LEFT JOIN tb_equipos eq ON pac.modelo_equipo = eq.id_equipo
LEFT JOIN tb_media_sat medfotos ON pac.cod_sat = medfotos.cod_sat AND medfotos.tipo = 1
LEFT JOIN tb_media_sat medvideos ON pac.cod_sat = medvideos.cod_sat AND medvideos.tipo = 2
WHERE pac.clinica = '".$idclinica."'
GROUP BY pac.id_sat
ORDER BY pac.fecha_sat DESC
My issue is that I am getting a wrong amount of COUNT items.
The real value for num_fotos should be 3 and for num_videos should be 2.
I am getting num_fotos = 6 and num_videos = 6.
EDIT
Table tb_sat
Table tb_media_sat
Sub-query will work better in your case like as follows:
SELECT pac.*, (SELECT COUNT(id_media_sat) FROM tb_media_sat WHERE cod_sat=pac.cod_sat AND tipo=1) AS num_fotos, (SELECT COUNT(id_media_sat) FROM tb_media_sat WHERE cod_sat=pac.cod_sat AND tipo=2) AS num_videos FROM tb_sat pac WHERE pac.clinica = '".$idclinica."' ORDER BY pac.fecha_sat DESC
Rest columns, please add yourself slowly slowly. I hope you will get correct output.

SQL error as a result of rewriting a query using subquery into a query using join

The original query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop.price_type_code,
fop.price_status_code,
fop.offering_id,
(SELECT fop1.price_amount from facility_offering_price fop1
WHERE fop.offering_id = fop1.Offering_Id
AND fop1.price_type_code = 5
AND fop1.price_status_code = 3
) as 'priceAmount'
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It produces a result of one entry.
The result query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop2.price_amount,
fop.price_type_code,
fop.offering_id,
fop2.offering_id
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
inner join
(select
fop1.offering_id,
fop1.price_amount
from facility_offering_price fop1
WHERE fop1.price_type_code = 5
AND fop1.price_status_code = 3
) fop2 on fop.offering_id = fop2.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It's result set is empty. However, an entry is found if I ask for fop1.price_status_code = 1.
Unable to wrap my head around this one I would appreciate your help.
Try using LEFT JOIN instead. The conversion from SELECT a, subquery AS val FROM ... to a join is more accurately reflected that way. The original query would return rows with NULL val when the subquery has no results; your version ends up omitting such rows completely.

combine tables with 1 to N relationship into 1 line of record with the last value of the N record

I need a modification of my previous post regarding
how to combine tables with 1 to many relationship into 1 line of record
how to combine tables with 1 to many relationship into 1 line of record
now my problem is my record has now 1 to many relationship. What I need to show is the last record only and combine it in a single line
tables tbl_equipment and tbl_warranty
and here is the desired output
here is the code I'm trying to implement
SELECT
a.equipmentid,
a.codename,
a.name,
a.labelid,
a.ACQUISITIONDATE,
a.description,
a.partofid,
w1.warrantyid as serviceidwarranty,
w1.startdate,
w1.enddate,
w2.warrantyid as productidwarranty,
w2.startdate,
w2.enddate,
s.equipstatusid,
l.equiplocationid FROM TBL_EQUIPMENTMST a
left JOIN tbl_equipwarranty w1
ON w1.equipmentid=a.equipmentid and w1.serviceproduct = 'service'
left JOIN tbl_equipwarranty w2
ON w2.equipmentid=a.equipmentid and w2.serviceproduct = 'product'
left join tbl_equipstatus s
on a.equipmentid = s.equipmentid
left join tbl_equiplocation l
on a.equipmentid = l.equipmentid WHERE a.equipmentid = '112'
I only want to show 1 record with the last value of warranty product and warranty service in the output. Can anyone guide me how to modify my code so that when I try join all the tables listed above can produce 1 record only with the last record of warranty as an output.
I am using firebird as a database. If you have a solution in mysql kindly tell me and ill try to find the counterpart in firebird.
with summary as(
select e.equipmentid ,e.Codename,e.Name,w.warrantyid ,w.Satartdate ,w.Enddate,w.warrantytype
from Eqp e
join Warranty w
on(w.equipmentid =e.equipmentid )
where w.warrantyid =3)
select *,w.warrantyid,w.Satartdate ,w.Enddate,w.warrantytype
from summary s
join Warranty w
on s.Satartdate =w.Satartdate and s.Enddate =w.Enddate
where w.warrantyid =4
after reading the comment of Barmar at the question for solution. I Figured out subquery can solve my problem. Subquery is a new word for me. I research on how to use subquery and came out with a solution below. you can correct me if my code is wrong or how to improve the performance of the query
SELECT
a.equipmentid,a.codename,a.name,a.labelid,a.ACQUISITIONDATE,a.description,a.partofid,
w1.warrantyid as serviceidwarranty,w1.startdate,w1.enddate,
w2.warrantyid as productidwarranty,w2.startdate,w2.enddate,
s.equipstatusid,
l.equiplocationid
FROM
TBL_EQUIPMENTMST a
left JOIN
(select first 1 *
from tbl_equipwarranty
where equipmentid='112' and serviceproduct = 'service'
order by warrantyid desc) w1 ON w1.equipmentid = a.equipmentid
and w1.serviceproduct = 'service'
left JOIN
(select first 1 *
from tbl_equipwarranty
where equipmentid = '112' and serviceproduct = 'product'
order by warrantyid desc) w2 ON w2.equipmentid = a.equipmentid
and w2.serviceproduct = 'product'
left join
(select first 1 *
from tbl_equipstatus
where equipmentid = '112'
order by equipstatusid desc) s on a.equipmentid = s.equipmentid
left join
(select first 1 *
from tbl_equiplocation
where equipmentid = '112'
order by equiplocationid desc) l on a.equipmentid = l.equipmentid
WHERE
a.equipmentid = '112'

Insert a parameter into Where Clause

I have this query which i want to get rank from the data on my database
set #urut:=0;
set #rankhrg:=0;
select #urut:=#urut+1 as urut, a.id_tender, b.nama_tender, b.nomor_tender, b.tgl_close1 as tgl_close,
(SELECT rankhrg
from (select sum(tot_harga) as hrg_twr, id_rekanan, id_tender, #rankhrg:=#rankhrg+1 as rankhrg from tb_real_barang where id_tender = s.id_tender group by id_rekanan) as rank_harga
left join tb_master_tender s on s.id_tender = b.id_tender
where rank_harga.id_rekanan = a.id_rekanan
order by rank_harga.hrg_twr asc) as ranking
from tb_real_tender a
left join tb_master_tender b on a.id_tender = b.id_tender
where a.id_rekanan = 1
order by convert(a.id_tender,unsigned) desc
i want to pass id_tender into the select inside the select when i want to get rankhrg :
select sum(tot_harga) as hrg_twr, id_rekanan, id_tender,
#rankhrg:=#rankhrg+1 as rankhrg
from tb_real_barang
where id_tender = s.id_tender
group by id_rekanan
but I always get error that said that s.id_tender is unknown in where clause.
can someone guide me how to pass the parameter into that insert?
thank you :)
You are not joining with that table tb_master_tender and neither it's present in outer query FROM clause. So, you need to do a JOIN separately for that inner query like below
select sum(trb.tot_harga) as hrg_twr,
trb.id_rekanan,
trb.id_tender,
#rankhrg:=#rankhrg+1 as rankhrg
from tb_real_barang trb
left join tb_master_tender s on trb.id_tender = s.id_tender
group by trb.id_rekanan

Is there a way to optimize this update query?

I have a master table called "parent" and a related table called "childs"
Now I run a query against the master table to update some values with the sum from the child table like this.
UPDATE master m SET
quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
count = (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;
Which works as expected but is not a good style because I basically make multiple SELECT querys on the same result. Is there a way to optimize that? (Making a query first and storing the values is not an option.
I tried this:
UPDATE master m SET (quantity1, quantity2, count) = (
SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;
but that doesn't work.
Update: Here is the solution, thanks to everbody:
You can do something like this:
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
master.count = 1
If you have only one child record at a time. However if you want to use a group function like SUM() in the joined table that doesn't work. Either you get a "Invalid use of group function" if you leave the "group by" part or a "You have an error in your sql syntax if you use "GROUP BY c.master_id"
-- This doesnt work :(
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
master.count = COUNT(c.*)
GROUP by c.master_id
The solution is to use JOIN with a subquery:
UPDATE master m
INNER JOIN
(
SELECT master_id,
SUM(quantity1) as quantity1,
COUNT(*) as count
FROM childs c
GROUP BY master_id
) c
ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
m.count = c.count
WHERE m.master_id = 666;
But since this pulls every row from the childtable the overhead would likely be bigger than using more subqueries like in the original sql. So you should add a WHERE clause to the joined table to get only the rows you need.
Another interesting approach is this syntax, which does the same as the JOIN with the WHERE clause but you should only use if if you want to update all rows with the same values and your subquery only returns one row, since the result from the subquery gets appended to the result and can be used like any column.
UPDATE master m,
(
SELECT SUM(c.quantity1) as sum_of_quantity,
COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
) as c
SET m.quantity1 = c.sum_of_quantity,
m.count = c.rowcount
WHERE m.master_id = 666;
Rewriting Lieven's solution to MySQL:
UPDATE master m
JOIN (
SELECT master_id
, SUM(quantity1) as quantity1
, SUM(quantity2) as quantity2
, COUNT(*) as count
FROM childs c
GROUP BY
master_id
) c
ON c.master_id = m.master_id
SET
m.quantity1 = c.quantity1
,m.quantity2 = c.quantity2
,m.count = c.count
WHERE m.master_id = 666;
I don't know if it is allowed in MySQL, but SQL Server allows you to use the result of a select in an update.
UPDATE master m SET
quantity1 = c.quantity1
, quantity2 = c.quantity2
, count = c.count
FROM master m
INNER JOIN (
SELECT master_id
, quantity1 = SUM(quantity1)
, quantity2 = SUM(quantity2)
, count = COUNT(*)
FROM childs c
WHERE master_id = 666
GROUP BY
master_id
) c ON c.master_id = m.master_id
You could select your data into a temporary table, and then update using that data.
If you also want to insert "new" data in the same roundtrip, look into INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...
If you already are doing inserts if row doesn't exist, then that would be redundant with this example.
example:
INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
FROM childs
GROUP BY master_id
ON DUPLICATE KEY UPDATE
m.quantity1 = q1,
m.quantity2 = q2,
m.count = c
NOTE! This is untested code, but I think it should be possible to backreference the select result in the UPDATE.
Syntax reference: http://dev.mysql.com/doc/refman/5.0/en/insert.html