Group by a pair - mysql

My Query
SELECT si1.itemno,si2.itemno,count(*)
FROM
(SELECT * FROM `saleitems` WHERE orgno = 9) as si1
INNER JOIN
(SELECT * FROM saleitems WHERE orgno =9) as si2
ON si1.invoiceno = si2.invoiceno AND si1.itemno != si2.itemno WHERE 1 GROUP BY si1.itemno,si2.itemno
Output:click to see output
But i expect 'group by (si1.itemno,si2.itemno) pair'. Like 3,4 and 4,3 should be grouped together in a single row.

The DBMS doesn't know that si1.itemno and si2.itemno are interchangeable, so as far as it's concerned si1.itemno=3,si2.itemno=4 is different from si1.itemno=4,si2.itemno=3.
The easiest way to eliminate those duplicates is to change the condition of your self-join so that it only finds the pairs with the IDs in order: just change the si1.itemno != si2.itemno condition to si1.itemno < si2.itemno.
Note that I referred to it as a "self-join", because although you currently have sub-queries, you don't need to, because you can just move the WHERE clauses to the final WHERE:
SELECT
si1.itemno,
si2.itemno,
count(*)
FROM
saleitems as si1
INNER JOIN
saleitems as si2
ON si1.invoiceno = si2.invoiceno
AND si1.itemno < si2.itemno
WHERE
si1.orgno = 9
AND si2.orgno = 9
GROUP BY
si1.itemno,
si2.itemno

Related

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.

mysql query group by totals

I am using the following query to retrieve the number of events per state from 2 tables that are linked by a userID.
SELECT state,COUNT(*) AS num
FROM tableUserInfo
WHERE userID IN (SELECT userID
FROM tableEvents
WHERE conditionOne = 1
AND conditionTwo = 2)
GROUP BY state
This query works correctly. My problem is that not all states have user entries, and I need the query to return 0 for those. I was wondering if there was a method such as joining or using an in clause, that would included a set of all states, making the query return 0 for any that didn't have entries in tableEvents?
Do you have a list of states? If not then this would give a list of all the states your database knows about:
SELECT DISTINCT state FROM tableUserInfo
....and enclosing this in brackets it can be dropped in place in the query below:
SELECT s.state, IFNULL(cnt, 0) AS num
FROM list_of_states s
LEFT JOIN (
SELECT state,COUNT(*) AS cnt
FROM tableUserInfo ui
INNER JOIN tableEvents te
ON ui.userId=te.userId
WHERE conditionOne = 1
AND conditionTwo = 2
GROUP BY state
) u
ON s.state=u.state;
Although in the absence of "list_of_states" it would be more efficient to do this:
SELECT ui.state, SUM(IF(te.userId IS NULL, 0, 1)) AS cnt
FROM tableUserInfo ui
LEFT JOIN tableEvents te
ON ui.userId=te.userId
AND te.conditionOne = 1
AND te.conditionTwo = 2
GROUP BY state;
As #raymond-nijland suggested you can use Left Join to include all states.
SELECT tableUserInfo.state,COUNT(tableUserInfo.*) AS num
FROM tableUserInfo Left Join tableEvents on tableUserInfo.userID = tableEvents.userID
WHERE tableEvents.conditionOne = 1 AND tableEvents.conditionTwo = 2
GROUP BY state

Inner join returning more results even after excluding parameters

My actuall query is:
SELECT rrr.extern_OD_id,
rrr.refund_it_amount,
rrr.refund_amount,
rrr.invoice_ref_7,
rrr.invoice_total_amount
FROM
(SELECT return.extern_OD_id,
return.refund_it_amt AS refund_it_Amount,
return.refund_amount,
billing_tbl.invoice_ref_7,
billing_tbl.invoice_total_amount
FROM
(SELECT rrrf.extern_OD_id,
sum(rrrf.refund_it_amt) AS refund_it_amt,
sum(rrrf.refund_amount/100) refund_amount
FROM rrr__refunds_fact rrrf
WHERE rrrf.refund_status = 'completed'
AND rrrf.refund_created_date_key >= '20150401'
AND rrrf.refund_mode IN('CREDIT_CARD',
'CREDIT_EMI',
'DDCHEQUE',
'DEBIT_CARD',
'GIFT_VOUCHER',
'ICICI',
'NETBANKING',
'back_to_source')
AND rrrf.refund_mode NOT IN ('GIFT_Card')
GROUP BY rrrf.extern_OD_id) RETURN
LEFT JOIN
(SELECT invoice_ref_7,
sum(invoice_total_amount) invoice_total_amount
FROM invoice_fact
WHERE invoice_type_key = 'receiv_note'
AND invoice_status_key NOT IN('voided',
'canceled',
'cancelled')
GROUP BY invoice_ref_7) billing_tbl ON RETURN.extern_OD_id =billing_tbl.invoice_ref_7
WHERE RETURN.refund_amount <> billing_tbl.invoice_total_amount
OR billing_tbl.invoice_ref_7 IS NULL) rrr
WHERE rrr.refund_it_amount >0
AND rrr.refund_amount >0
But when I try to join another table and exclude some parameters, it gives me more records than records from table srrr__refunds_fact.
It gives many records from the new joined table i.e. Payment_TBL which are not present in rrr__refunds_fact.
Can you please tell me where I am going wrong.
As per my understanding if the first query is returning n records, after joining the new records should be n or < n
SELECT rrr.extern_OD_id,
rrr.refund_it_amount,
rrr.refund_amount,
rrr.invoice_ref_7,
rrr.invoice_total_amount
FROM
(SELECT return.extern_OD_id,
return.refund_it_amt AS refund_it_Amount,
return.refund_amount,
billing_tbl.invoice_ref_7,
billing_tbl.invoice_total_amount
FROM
(SELECT rrrf.extern_OD_id,
sum(rrrf.refund_it_amt) AS refund_it_amt,
sum(rrrf.refund_amount/100) refund_amount
FROM rrr__refunds_fact rrrf
JOIN Payment_TBL paymt ON paymt.payment_ref_num_2 = rrrf.extern_OD_id
WHERE rrrf.refund_status = 'completed'
AND rrrf.refund_created_date_key >= '20150401'
AND rrrf.refund_mode IN('CREDIT_CARD',
'CREDIT_EMI',
'DDCHEQUE',
'DEBIT_CARD',
'GIFT_VOUCHER',
'ICICI',
'NETBANKING',
'back_to_source')
AND rrrf.refund_mode NOT IN ('GIFT_Card')
AND paymt.payment_method_key NOT IN ('WALLET')
AND paymt.payment_ref_num_4 NOT IN ('PRICE_REFUND')
GROUP BY rrrf.extern_OD_id) RETURN
LEFT JOIN
(SELECT invoice_ref_7,
sum(invoice_total_amount) invoice_total_amount
FROM invoice_fact
WHERE invoice_type_key = 'receiv_note'
AND invoice_status_key NOT IN('voided',
'canceled',
'cancelled')
GROUP BY invoice_ref_7) billing_tbl ON RETURN.extern_OD_id =billing_tbl.invoice_ref_7
WHERE RETURN.refund_amount <> billing_tbl.invoice_total_amount
OR billing_tbl.invoice_ref_7 IS NULL) rrr
WHERE rrr.refund_it_amount >0
AND rrr.refund_amount >0

How to query mysql to select and group by multiple values

I'm trying to select and group by all the contentid values of the table below where the match criteria can be several different values.
the contentid values actually represent cars, so I need to select [and group by] all the contentis where the values are 'GMC' and the values are 'sedan' and the value is 'automatic.
i.e. I'm trying to select all the GMC sedans with an automatic transmission.
a query like this fails [obviously]:
select * from modx_site_tmplvar_contentvalues WHERE
`value` = 'gmc' and
`value` = 'tacoma'
group by contentid
I have no idea how to create a query like that. Any suggestions?
You need to "pivot" these data on "tmplvarid", but unfortunately for you MySQL doesn't have a PIVOT statement like other RDBMS. However, you can pivot it yourself by joining in the table multiple times for each variable you care about:
SELECT
contents.contentid,
transmission.value as transmission,
type.value as type,
make.value as make
FROM
(SELECT DISTINCT contentid FROM modx_site_tmplvar_contentvalues) AS contents
LEFT JOIN
modx_site_tmplvar_contentvalues AS transmission
ON contents.contentid = transmission.contentid
AND transmission.tmplvarid = 33 -- id for transmission
LEFT JOIN
modx_site_tmplvar_contentvalues AS make
ON contents.contentid = make.contentid
AND make.tmplvarid = 13 -- id for make
LEFT JOIN
modx_site_tmplvar_contentvalues AS type
ON contents.contentid = type.contentid
AND type.tmplvarid = 17 -- id for type
WHERE
type.value = 'sedan'
AND make.value = 'GMC'
AND transmission.value = 'automatic'
You can expand this with additional joins for other criteria such as year (id 15) or mileage (id 16).
If you need to use the value only, you could try:
SELECT DISTINCT
contents.contentid,
transmission.value as transmission,
type.value as type,
make.value as make
FROM
(SELECT DISTINCT contentid FROM modx_site_tmplvar_contentvalues) AS contents
INNER JOIN
modx_site_tmplvar_contentvalues AS transmission
ON contents.contentid = transmission.contentid
AND transmission.value = 'automatic'
INNER JOIN
modx_site_tmplvar_contentvalues AS make
ON contents.contentid = make.contentid
AND make.value = 'GMC'
INNER JOIN
modx_site_tmplvar_contentvalues AS type
ON contents.contentid = type.contentid
AND type.value = 'sedan'
In any case, make sure you have an index on the value column; these queries are going to get slow.
please try this:
SELECT *
FROM modx_site_tmplvar_contentvalues t1 INNER JOIN modx_site_tmplvar_contentvalues t2 ON t1.contentid = t2.content_id
WHERE
t1.`value` = 'gmc'
AND t2.`value` = 'tacoma';
You can do this with a group by. This is the most flexible in terms of expressing the conditions. In MySQL, multiple joins will often perform better:
select contentid
from modx_site_tmplvar_contentvalues
group by contentid
having sum(`value` = 'gmc') > 0 and
sum(`value` = 'tacoma') > 0;
This is always false:
`value` = 'gmc' and
`value` = 'tacoma'
Instead, use OR:
`value` = 'gmc' OR
`value` = 'tacoma'
In a condition "and" means "this and this is true at the same time". If you want all foos and all bars, then your condition is "foo OR bar".
EDIT:
To select groups containing your values, you can write subqueries:
SELECT DISTINCT name FROM table WHERE name IN (SELECT name FROM table WHERE value='value1') AND name IN (SELECT name FROM table WHERE value='value2')

MySQL display only unique rows

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.