Basically what I'm trying to do is to get a list of purchases from one table and check if there is any attachments associated with the purchase and display 1 or 0 if there is/isn't an attachment found.
SELECT
CONCAT('#', LPAD(a.id, 5, '0')) as 'code',
a.id, a.value_total, DATE_FORMAT(a.date, '%d/%m/%Y') as 'date',
b.name as 'store',
CASE WHEN a.notes IS NOT NULL
THEN 1
ELSE 0
END AS notes
FROM
tb_purchase a,
tb_store b
WHERE a.id_store = b.id
AND a.id = :id_purchase // This line can be removed to get all the purchases or leave here to get just one with that specific ID
then I have a table called ts_purchase_att where I store the attachments relation between purchases and attachments. The table has this structure:
id_purchase | id_attachment
32 | 47
32 | 127
33 | 68
38 | 97
I don't need to get the attachments, just check if there is at least one attachment related to that purchase.
I tried using something like this but it doesn't work
LEFT JOIN
( SELECT 1 FROM ts_purchase_att c WHERE c.id_purchase = a.id ) as attachment
What am I doing wrong?
Use JOIN syntax consistently, it has been standard since 1992. Don't use the outdated comma-style joins. Mixing the two syntax styles is causing a real problem in your case.
The reason is that the JOIN operation has a higher precedence than the , join. So when you do this type of query:
FROM a, b LEFT OUTER JOIN c ON a.id = c.id_purchase
It first tries to evaluate the b LEFT OUTER JOIN c, but you're referencing the first table a in the join condition. MySQL hasn't even made that table alias yet, so it returns an error on references to columns belonging to a.
This is documented here: https://dev.mysql.com/doc/refman/8.0/en/join.html
JOIN has higher precedence than the comma operator (,), so the join expression t1, t2 JOIN t3 is interpreted as (t1, (t2 JOIN t3)), not as ((t1, t2) JOIN t3). This affects statements that use an ON clause because that clause can refer only to columns in the operands of the join, and the precedence affects interpretation of what those operands are.
See that section of the manual for more details and examples.
Here's the way to do the outer join combined with inner joins using JOIN syntax:
SELECT IF(c.id_purchase IS NULL, 0, 1) AS attachment_found
FROM tb_purchase a,
INNER JOIN tb_store b ON a.id_store = b.id
LEFT OUTER JOIN ts_purchase_att c ON a.id = c.id_purchase
You need a LEFT JOIN:
SELECT DISTINCT
CONCAT('#', LPAD(a.id, 5, '0')) as 'code',
a.id, a.value_total, DATE_FORMAT(a.date, '%d/%m/%Y') as 'date'
CASE
WHEN b.id IS NOT NULL THEN 1
ELSE 0
END AS notes
FROM tb_purchase a LEFT JOIN tb_store b
ON a.id_store = b.id
When there is no match in the table tb_store then b.id is null.
Related
I am trying to create an SQL query, but when I left join an additional table (c) with conditions it reduces the number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.NNaziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.id
ORDER BY a.idArtikla ASC
Query below works great. I would like to keep the records I get, but I would like to JOIN another table. Once I do that it reduces number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal
WHERE c.status = 1
ORDER BY a.idArtikla ASC
How I can show all rows from first query and attach table radni_nalozi c with where condition that sums only quantity with status = 1?
try this
With Outer Join, if you put in where clause then it will be treated as inner join.
$stmt = "SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla, norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFTJOIN radni_nalozi c ON a.idArtikla = c.idArtikal and c.status = 1
ORDER BY a.idArtikla ASC";
You outer-join radni_nalozi. This means that in case there is no match, the original row is kept and all radni_nalozi columns are null. Then you limit your results by:
WHERE c.status = 1
This dismisses all outer-joined rows, because their status is null. You have turned your outer join into an inner join.
What you want instead is the condition to be part of the join clause:
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal AND c.status = 1
But as P.Salmon just pointed out, your query is invalid, because of a malformed aggregation. MySQL should raise a syntax error, but obviously you have not SET sql_mode = 'ONLY_FULL_GROUP_BY', which you should in order to have the DBMS prevent you from writing such invalid queries. With sum(c.kolicina) and no GROUP BY clause, you tell the DBMS to limit your results to a single row containing the sum. But what values for a.id, a.naziv, etc. is the DBMS supposed to show then?
It seems you want to join the radni_nalozi sums:
SELECT
ar.id,
ar.naziv as nazivOperacije,
a.Naziv as nazivArtikla,
a.sifra as sifraArtikla,
a.norma,
ar.idArtikla,
rn.sum_kolicina AS kolRadnogNaloga
FROM artikli_rad ar
JOIN artikli a ON a.id = ar.idArtikla
LEFT JOIN
(
SELECT idArtikal, SUM(kolicina) AS sum_kolicina
FROM radni_nalozi
WHERE status = 1
GROUP BY idArtikal
) rn ON rn.idArtikla = ar.idArtikal
ORDER BY ar.idArtikla ASC;
I have also used mnemonic alias names. names like a, b, and c don't make the query more readable as alias names are supposed to do, but make it less readable.
I also turned the outer join on artikli into an inner join, because there must be no artikli_rad row without a match in artikli in a properly set up database.
I want to perform a left join on two tables. The field I will join by is an email address. As the table I on the left has two email fields which may be different, I want that if the joining by the first email fails and returns null values, perform a second join on the other email field. Lastly, I want to throw away those entries which have not been matched with any of the two joins.
I have thought of doing something like this:
SELECT *
FROM a
LEFT JOIN b
ON
a.address1 = b.email
OR a.address2 = b.email
However, this returned an error message saying LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join.
What is the correct way of achieving this?
OR in JOIN conditions makes is really hard to optimize queries. So, BigQuery makes it hard to use OR.
I think you can rephrase the query by doing:
SELECT *
FROM a CROSS JOIN
UNNEST(ARRAY[address1, address2]) address LEFT JOIN
b
ON address = b.email;
You might be able to phrase your logic using a union:
SELECT * FROM a LEFT JOIN b ON a.address1 = b.email
UNION
SELECT * FROM a LEFT JOIN b ON a.address2 = b.email;
Consider below (BigQuery) approach
SELECT * EXCEPT(row_key, priority)
FROM (
SELECT *, TO_JSON_STRING(a) row_key, IF(b.email IS NULL, 3, 1) priority
FROM `project.dataset.tableA` a LEFT JOIN `project.dataset.tableB` b
ON a.address1 = b.email
UNION ALL
SELECT *, TO_JSON_STRING(a), IF(b.email IS NULL, 3, 2)
FROM `project.dataset.tableA` a LEFT JOIN `project.dataset.tableB` b
ON a.address2 = b.email
)
WHERE true
QUALIFY ROW_NUMBER() OVER(PARTITION BY row_key ORDER BY priority NULLS LAST) = 1
Or you can use less verbose / more generic (so you can avoid redundant code fragments which can be useful if you need more than just two conditions) version of above
SELECT * EXCEPT(row_key, priority)
FROM (
SELECT a.*, b.*, TO_JSON_STRING(a) row_key, IF(b.email IS NULL, 3, c.priority) priority
FROM `project.dataset.tableA` a,
UNNEST([STRUCT(address1 as address, 1 as priority), (address2, 2)]) c
LEFT JOIN `project.dataset.tableB` b
ON c.address = b.email
)
WHERE TRUE
QUALIFY ROW_NUMBER() OVER(PARTITION BY row_key ORDER BY priority NULLS LAST) = 1
My query has multiple column being selected but now i want when no returned I want some dummy value to be returned.
For example in the following query:
SELECT a.abc, b.def, c.ghi
FROM comp a, damp b, champ c, omp d
WHERE a.id=b.id
and b.id=c.id
and c.id= d.id
ORDER BY a.abc desc
if there is no row returned I wanted to display atleast one column with some value, can somebody suggest me how can I achieve this.
I have already gone through some suggestion but none worked. Any help would be appreciated.
In oracle you could do something like this:
WITH mybigselect AS
(
SELECT a.abc, b.def, c.ghi
FROM comp a
JOIN damp b ON a.id = b.id
JOIN champ c ON b.id = c.id
JOIN omp d ON c.id = d.id
ORDER BY a.abc desc
)
SELECT * FROM mybigselect
UNION ALL
SELECT 'Nothing found', NULL, NULL FROM mybigselect
WHERE NOT EXISTS (SELECT * FROM mybigselect)
Note 1: that both rows in the UNION ALL needs to return columns of the same datatype. You can't return a number in the first column of SELECT * FROM mybigselect and "nothing found" in the query after UNION ALL
Note 2: rewrote the query using ANSI-JOIN style syntax.
I would recommend:
WITH cte as (
SELECT a.abc, b.def, c.ghi
FROM comp do JOIN
damp d
ON d.id = co.id JOIN
champ c
ON ch.id = d.id
omp o
ON o.id = ch.id
)
SELECT *
FROM cte
UNION ALL
SELECT 'Some value', NULL, NLL
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM cte)
ORDER BY abc DESC;
Notes:
The value has to be compatible with the type of the column. So, if abc is not a string, then 'Some value' is not appropriate. You haven't provided enough information to determine what value should be in which column.
The ORDER BY should be in the outermost query, not the CTE.
Never use comas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
This version uses meaningful table aliases (table name abbreviations) rather than arbitrary letters.
I have 3 tables with following columns.
Table: A with column: newColumnTyp1, typ2
Table: B with column: typ2, tableC_id_fk
Table: C with column: id, typ1
I wanted to update values in A.newColumnTyp1 from C.typ1 by following logic:
if A.typ2=B.typ2 and B.tableC_id_fk=C.id
the values must be distinct, if any of the conditions above gives multiple results then should be ignored. For example A.typ2=B.typ2 may give multiple result in that case it should be ignored.
edit:
the values must be distinct, if any of the conditions above gives multiple results then take only one value and ignore rest. For example A.typ2=B.typ2 may give multiple result in that case just take any one value and ignore rest because all the results from A.typ2=B.typ2 will have same B.tableC_id_fk.
I have tried:
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.typ2
it gives me a result of table with two columns typ1,typ2
My logic was, I will then filter this new table and compare the type2 value with A.typ2 and update A.newColumnTyp1
I thought of something like this but was a failure:
update A set newColumnTyp1= (
SELECT C.typ1 from
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.type2
where A.typ2=B.typ2);
I am thinking of an updateable CTE and window functions:
with cte as (
select a.newColumnTyp1, c.typ1, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set newColumnTyp1 = typ1
where cnt > 1
Update: if the columns have the same name, then alias one of them:
with cte as (
select a.typ1, c.typ1 typ1c, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set typ1 = typ1c
where cnt > 1
I think I would approach this as:
update a
set newColumnTyp1 = bc.min_typ1
from (select b.typ2, min(c.typ1) as min_typ1, max(c.typ1) as max_typ1
from b join
c
on b.tableC_id_fk = c.id
group by b.type2
) bc
where bc.typ2 = a.typ2 and
bc.min_typ1 = bc.max_typ1;
The subquery determines whether typ1 is always the same. If so, it is used for updating.
I should note that you might want the most common value assigned, instead of requiring unanimity. If that is what you want, then you can ask another question.
I am using the SELECT statement below to join the property table with the epc table. Not always is the EPC available for a property. I also want the epc table if the property does not exist.
SELECT p.dateAdded, p.paon, p.saon, p.street, p.locality, p.townCity, p.district, p.county, p.propertyType,
p.propertyType, p.oldNew, p.postcode, p.tenure, p.ppd, p.bedrooms, p.bathrooms, p.receptions, p.lastSalePrice, p.lastTransferDate,
e.INSPECTION_DATE, e.TOTAL_FLOOR_AREA, e.CURRENT_ENERGY_RATING, e.POTENTIAL_ENERGY_RATING, e.CURRENT_ENERGY_EFFICIENCY, e.POTENTIAL_ENERGY_EFFICIENCY,
e.PROPERTY_TYPE
FROM property p
LEFT JOIN epc e ON p.postcode = e.POSTCODE AND CONCAT(p.paon, ', ', p.street) = e.ADDRESS1
WHERE p.paon = 8 AND p.postcode = "TS6 9LN"
ORDER BY e.INSPECTION_DATE, p.lastTransferDate DESC
LIMIT 1
Is it possible to select both tables but if 1 doesn't exist, select the 1 that does?
You need a FULL OUTER JOIN. Unfortunately MySQL does not implement this part of the SQL standard. You can simulate a full outer join with two outer joins, though, but it becomes long, and possibly quite cumbersome and error prone.
For example:
select a.col1, b.col2
from table_a a
LEFT join table_b b on ...
union -- here we union both outer joins
select a.col1, b.col2
from table_a a
RIGHT join table_b b on ...
In the second SELECT the table roles are inverted, since it uses a RIGHT JOIN instead of a LEFT JOIN.