MySQL LEFT OUTER JOIN doesn't work - mysql

I have a problem with my mysql output on the workbench. I'm trying to get the company(qualified_name) together with an timestamp(updated).
Company and the timestamp are in different tables(the headers are further down). Ignore the id's there were only for me to compare
SELECT entity_id, c.id, o.id, o.updated,
res_companies.qualified_name
FROM str_entities as o
JOIN str_entities c
ON c.id = o.owner_id
AND c.client = "client"
LEFT JOIN res_companies
ON entity_id = c.id
where o.status = "active"
AND o.entity_type_id = 7
MySql Workbench gives me that output
Here is the table header for res_companies
And here is the heder from str_entities

try this and tell us what you get
Select count(*) from res_companies rc
Where exists (Select * from str_entities se
Where id = rc.entity_id
and client = "client"
and exists (Select * from str_entities
Where owner_id = se.id))
Are there any records there ?
If the result is zero (0), then the last column (from the left join) is null because there are simply no records in the table that match the criteria you have specified.

Related

SQL Complex update query filter distinct values only

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.

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

Collapse rows with equal values in particular columns in one

I have a table with deals data written in it. I need to query said deals while joining on other tables, however d.originalorderid is not a unique entry and I get several duplicate entries. I want to:
For each unique d.originalorderid select one row
This row should be most recent (largest ID)
How would I go about this? This is the query I have right now.
SELECT d.id,
d.date,
d.ip, d.panmask,
d.merchantorderid,
d.amount,
d.cardholder,
d.bankhumanname,
d.cardtypeid,
d.bankcountrycode,
d.usercountrycode,
mc.paymentkey as merchantname,
dt.status,
d.merchantcontract,
dt.tag,
d.originalorderid,
ds.refnumber,
ds.dealauthcode,
mc.processingid,
pc.Name as processing,
d.customparams
FROM Deal as d
LEFT JOIN MerchantContract as mc ON mc.Id = d.MerchantContract
LEFT JOIN DealTrace as dt ON d.Id = dt.DealId
AND dt.id = (SELECT MAX(id)
FROM DealTrace WITH (nolock)
WHERE DealId = d.id)
LEFT JOIN DealSummary ds ON d.Id = ds.DealId
AND ds.id = (SELECT MAX(id)
FROM DealSummary WITH (nolock)
WHERE DealId = d.id)
LEFT JOIN Processing pc on mc.ProcessingId = pc.id
WHERE (d.MerchantContract IN ('12'))
ORDER BY ID desc OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY
If I understand the requirement, instead of joining to the Deal table, join to a correlated subquery on it which returns the deal with the highest deal id which has the same original order id. Test it but I think its OK..
SELECT ....
FROM
(SELECT *
FROM Deal d1
WHERE d1.Id=(SELECT MAX(Id)
FROM Deal d2
WHERE d2.OriginalOrderId=d1.OriginalOrderId)) d
LEFT JOIN MerchantContract as mc ON mc.Id = d.MerchantContract
etc etc

MySQL query to get latest value from child tables

I have a table for clients and three tables of related details as seen from this Fiddle.
What I would like to do is get the clients' data and their related rows for the latest contact date.
I would like to create a view from that query and be able to filter by operator id. I am always getting a wrong number of rows to what I expect.
Below is my query, can someone help me figure out why it returns incorrect results?
select c.*,cs.client_status,cn.client_note,cd.contact_date from clienti c
left join client_status cs on c.id = cs.client
left join client_notes cn on c.id = cn.client
left join client_contact_date cd on c.id = cd.client
where
(
(
`cd`.`contact_date` = (select max(`client_contact_date`.`contact_date`) from `client_contact_date` where `client_contact_date`.`client` = `c`.`id`)
)
AND
(
`cn`.`mod_time` = (select max(`client_notes`.`mod_time`) from `client_notes` where `client_notes`.`client` = `c`.`id`)
)
AND
(
`cs`.`mod_time` = (select max(`client_status`.`mod_time`) from `client_status` where `client_status`.`client` = `c`.`id`)
)
)
I would suggest you break this up into smaller pieces and then begin to put the joins together.
At the core of everything that you want is the latest contact_date/status/note for each client. You can get each of those using aggregation:
SELECT client, MAX(contact_date) AS latestContact
FROM client_contact_date
GROUP BY client;
SELECT client, MAX(mod_time) AS latestNote
FROM client_notes
GROUP BY client;
SELECT client, MAX(mod_time) AS latestStatus
FROM client_status
GROUP BY client;
The first query can easily be outer joined to the client table to get the client information since there should only be one row for each client. For the last two of those subqueries, you'll have to join back to the original tables (notes, status) to get the information like this, because the row must be matched by time as well:
SELECT n.client, n.client_note, n.mod_time
FROM client_notes n
JOIN(
SELECT client, MAX(mod_time) AS latestNote
FROM client_notes
GROUP BY client) t ON t.client = n.client AND t.latestNote = n.mod_time;
SELECT s.client, s.client_status, s.mod_time
FROM client_status s
JOIN(
SELECT client, MAX(mod_time) AS latestStatus
FROM client_status
GROUP BY client) t ON t.client = s.client AND t.latestStatus = s.mod_time;
Once you have all of those things, you can use outer joins to bring the latest rows of information together:
SELECT c.id, c.clientName, c.operators, cd.latestContact, cn.client_note, cn.latestNote, cs.client_status, cs.latestStatus
FROM clienti c
LEFT JOIN(
SELECT client, MAX(contact_date) AS latestContact
FROM client_contact_date
GROUP BY client) cd ON cd.client = c.id
LEFT JOIN(
SELECT n.client, n.client_note, n.mod_time AS latestNote
FROM client_notes n
JOIN(
SELECT client, MAX(mod_time) AS latestNote
FROM client_notes
GROUP BY client) t ON t.client = n.client AND t.latestNote = n.mod_time) cn ON cn.client = c.id
LEFT JOIN(
SELECT s.client, s.client_status, s.mod_time AS latestStatus
FROM client_status s
JOIN(
SELECT client, MAX(mod_time) AS latestStatus
FROM client_status
GROUP BY client) t ON t.client = s.client AND t.latestStatus = s.mod_time) cs ON cs.client = c.id;
I would like to make one comment though. I see that you are storing operators as a list. This breaks normalization, and is generally a very bad idea. For me information, see Is storing a delimited list in a database column really that bad?
Here is an SQL Fiddle example with my query.

Select fields of a table with a conditional WHERE clause

I have 3 left joined tables in MySql.
contratos, which stores customer data
funcionario, which stores employee data
cobranca, which stores every customer charge.
I want to generate a report based on charge status. But each customer charge has many status, and i want to retrieve the current status.
The following code returns the last update from cobrancas table.
SELECT cob.status, cob.created, con.data_venda, cpn.contrato, con.razao_social, con.cnpj, con.valor, f.nick
FROM cobrancas cob
LEFT JOIN contratos con
ON c.id = cob.contrato
LEFT JOIN funcionarios f
ON f.id = cob.cobrador
WHERE 1=1
ORDER BY cob.created DESC
LIMIT 1
But it returns without a status filter. If i put a WHERE clause like cob.status = 'x', it returns the last record with this status, but it may not be the current. So how can i check if cob.status is the current status in WHERE clause to decide if i will put it in the list? Something like:
WHERE IF(cob.status == the last status inserted AND cob.status == 'x')
Can you understand what i want to do? Thank you.
One solution to use subquery
select * from
(
SELECT cob.status, cob.created, con.data_venda, cpn.contrato, con.razao_social, con.cnpj, con.valor, f.nick
FROM cobrancas cob
LEFT JOIN contratos con
ON c.id = cob.contrato
LEFT JOIN funcionarios f
ON f.id = cob.cobrador
WHERE 1=1
ORDER BY cob.created DESC
LIMIT 1
) where status = 'X'