I have some tables roughly like so:
Client:
id
name
Employee
id
name
Email
id
to : Client [ForeignKey]
from : Employee [ForeignKey]
EmailStats (Tracks the stats for a particular single email)
id
email : Email [OneToOne]
curse_words : 10
What I want to do: I want to fetch all the employees that have written at least one email to a single client, along with the number of times they've cursed in any of their emails to that single client, i.e. for a particular Client return
[
('name' : 'Paul', 'total_curses' : 255),
('name' : 'Mary', 'total_curses' : 10),
]
What I've tried:
My understanding of SQL is quite weak as I'm used to using ORM's. I'm having trouble understanding how the normal retrieval of Employees links into the counting of the curse words. Here's what I've done (be kind!):
SELECT DISTINCT (
SELECT SUM(EmailStats.curse_words)
FROM EmailStats
INNER JOIN (
SELECT Email.id
FROM Email
INNER JOIN Employee
ON Email.from = Employee.id
WHERE Email.to = 5 // Assuming 5 is the client's id
) filtered_emails ON EmailStats.email = filtered_emails.id
) AS 'total_curses', Employee.name
FROM Employee
INNER JOIN Email
ON Email.from = Employee.id
WHERE Email.to = 5 // Assuming 5 is the client's id
ORDER_BY 'total_curses'
This isn't working - it seems to fetch the correct Employees (those who have sent to the Client) but the curses count seems to be the total for all emails to that Client instead of just those curses from that Employee.
I've got a feeling that I'm gravely misunderstanding something here, so if anyone could provide an example of how to succesfully go about this I'd appreciate some pointers.
You want to group the result of joining your tables:
SELECT Employee.name, SUM(EmailStats.curse_words) total_curses
FROM Email
JOIN EmailStats ON EmailStats.email = Email.id
JOIN Employee ON Employee.id = Email.from
WHERE Email.to = 5
GROUP BY Employee.id
ORDER BY total_curses DESC
SELECT em.name, sum(s.curse_words) AS total_curses
FROM employee em
JOIN email e ON e.from = em.id
LEFT JOIN emailstats s ON s.email = e.id
WHERE e.to = $the_one_client
GROUP BY em.name
ORDER BY total_curses DESC;
I use a LEFT JOIN to make sure, because there does not seem to be a guarantee, that a matching row in emailstats actually exists.
Related
I'm not good with sql so i need help exporting my customer's first names, email address, their country and if possible last times they had access to the store.
I managed to get email and first name by using this query :
SELECT firstname, lastname, email, active FROM psdb_customer
From what i understood, since the other data is stored in a different db table, i should use a join to get data from both tables but i haven't been able to figure out how
Any help is welcome
In the past, I helped someone with something similar in the PrestaShop forum. This query should work, just remember that to get a country of a customer they should have at least one address registered and of course I'm using the default db prefix:
SELECT a.`firstname`,
a.`lastname`,
a.`email`,
(SELECT c.`date_add`
FROM `ps_guest` g
LEFT JOIN `ps_connections` c ON c.`id_guest` = g.`id_guest`
WHERE g.`id_customer` = a.`id_customer`
ORDER BY c.`date_add` DESC LIMIT 1) AS `last_activity`,
(SELECT cl.`name`
FROM `ps_address` ad
LEFT JOIN `ps_country_lang` cl ON cl.`id_country` = ad.`id_country`
WHERE ad.`id_customer` = a.`id_customer`
ORDER BY ad.`id_address` DESC LIMIT 1) AS `country_name`
FROM `ps_customer` a
Rolige's answer is what i was looking for.
Here is another query that allows filtering the results by country (using the id_country)
SELECT SQL_CALC_FOUND_ROWS a.`id_address`,
a.firstname as firstname,
a.lastname as lastname,
cl.id_country as country, cl.`name` as country
FROM `ps_address` a
LEFT JOIN `ps_country_lang` cl ON (cl.`id_country` = a.`id_country`
AND cl.`id_lang` = 1)
LEFT JOIN `ps_customer` c ON a.id_customer = c.id_customer
WHERE 1 AND a.id_customer != 0
AND c.id_shop IN (1)
AND a.`deleted` = 0
AND cl.`id_country` = '8'
ORDER BY a.`id_address` ASC
I am making a user status list of the following format "A like B's XXX". A and B are both registered users and have firstname and lastname and user id. How to join the status table with the user table twice to get the names of the two users? Thank you.
SELECT "SQACTION"."TIMECREATED",
"SQWORDLIST".*,
"SUBJECT"."FIRSTNAME" subject_fn,
"SUBJECT"."LASTNAME" subject_ln,
author.firstname author_fn,
author.lastname author_ln
FROM "SQACTION"
INNER JOIN "SQWORDLIST"
ON SQACTION.ACTION = SQWORDLIST.GUID
INNER JOIN "SQUSER" SUBJECT
ON SQACTION.SUBJECT = SUBJECT.GUID
LEFT JOIN SQDOCUMENT
ON SQACTION.ENTITY = SQDOCUMENT.GUID
LEFT JOIN SQUSER AUTHOR
ON SQDOCUMENT.AUTHORID = AUTHOR.GUID
WHERE (SUBJECT.GUID = 'B4D3BF632C0C4DB3AB01C8B284069D8F')
OR (SUBJECT.GUID IN ('67882AF3FA3C4254AF9A12CA0B0AB6E4',
'6A4B52FE233444838AACFE2AFFE4D38F',
'8CA3FB9061FF4710B51F1E398D3D1917'))
ORDER BY "TIMECREATED" DESC
This is what I have tried. Thank you.
You need to include the table name twice in the FROM clause, and use an alias so you can specify which fields from each instance of the table are used in the ON statement. You didn't provide enough details in your question to give an exact example, so here is something more general.
UserTable, with ID & Name
RegTable, with UserID, and SponsorID
select ut1.name as [User],
ut2.name as [Sponsor]
from UserTable ut1
inner join RegTable rt on ut1.id = rt.userid
inner join UserTable ut2 on rt.sponsorid = ut2.id
do you mean something like, status have two field links to user table?
select user_a.first_name as user_a_first_name, user_b.first_name as user_b_first_name, status.status_name
from status
left join users as user_a on user_a.id = status.user_from_id
left join users as user_b on user_b.id = status.user_to_id
I am trying to output a list of contact names with their phone number and email address for a given company.
The problem I am facing is getting it to output a contact based on the following criteria:
Contacts may or may not have a name, email or telephone, but they must have at least one of these to appear in the results.
There can be more than one contact per company.
There can be more than one email address and/or telephone number per contact.
There is a primary flag on contacts, so if there is more than one contact and one of them is primary, it should pick that one instead of the other non-primary one.
I have tried the following for getting the contacts name but with no success:
SELECT entity_details.name,
COALESCE(
(SELECT entity_contacts.name FROM entity_contacts
WHERE entity_contacts.entityRef = entity_details.id
ORDER BY entity_contacts.isPrimary = 1),
(SELECT entity_contacts.name FROM entity_contacts
WHERE entity_contacts.entityRef = entity_details.id)
)
AS contact
FROM entity_details
WHERE entity_details.ownerRef = ?
This is the closest thing I can get to but I am unsure of if it's correct or not, and it doesn't prioritise primary contacts, it just selects any and groups on the entityRef to remove duplicates:
SELECT
entity_details.name, entity_contacts.name AS contact,
entity_contacts_telephones.tel, entity_contacts_emails.email
FROM entity_details
LEFT JOIN entity_contacts ON entity_details.id = entity_contacts.entityRef
LEFT JOIN entity_contacts_telephones ON entity_contacts.id = entity_contacts_telephones.contactRef
LEFT JOIN entity_contacts_emails ON entity_contacts.id = entity_contacts_emails.contactRef
WHERE entity_details.ownerRef = ?
GROUP BY entity_contacts.entityRef
LIMIT ?, ?
All tables are Innobd, the ones I am working with are in the above edit. All references etc have indexes on where nesisary.
In entity_details there around about 13000 rows, 12000 in entity_contacts, and a few 1000 in entity_contacts_telephones and entity_contacts_emails.
I thought the following would work but it doesn't:
LEFT JOIN entity_contacts_telephones
ON entity_contacts.id = entity_contacts_telephones.contactRef
AND COALESCE(entity_contacts_telephones.isPrimary = 1, 0)
This may work (not sure mainly because it's not clear how many rows per entity you can have in each table):
SELECT
entity_details.name,
( SELECT entity_contacts.name
FROM entity_contacts
WHERE entity_contacts.entityRef = entity_details.id
ORDER BY entity_contacts.isPrimary DESC
LIMIT 1
) AS contact
FROM entity_details
WHERE entity_details.ownerRef = ?
You probably need a join - of the [greatest-n-per-group] type:
SELECT
d.name,
...
c.whatever
...
FROM
entity_details AS d
JOIN
entity_contacts AS c
ON c.PK = --- the PRIMARY KEY of contacts table
( SELECT cc.PK
FROM entity_contacts AS cc
WHERE cc.entityRef = d.id
ORDER BY cc.isPrimary DESC
LIMIT 1
)
WHERE d.ownerRef = ?
An index on (entityRef, isPrimary) would help improving peformance.
I'm looking for a more efficient way to build this query (double subqueries make me cringe):
SELECT contact_id FROM (
SELECT * FROM (
SELECT mr.contact_id, di.district
FROM recipients mr
JOIN address a ON mr.contact_id = a.contact_id
JOIN district_values di ON a.id = di.entity_id
WHERE mr.mid = 29
ORDER BY di.district DESC ) addrSingle
GROUP BY mr.contact_id ) addrNull
WHERE di.district IS NULL
Let me explain what's going on here.
Recipients holds a list of contacts. Each contact may have multiple addresses. Each address has a related district_values table. I need to retrieve contacts where the district_values.district column is null for ALL addresses.
For example:
Contact A
Address 1.district = 4
Address 2.district = null
= don't include
Contact B
Address 1.district = null
= include
Contact C
Address 1.district = null
Address 2.district = 3
= don't include
The logic of my existing query is as follows:
retrieve contacts with related addresses and districts, order so that any addresses with a non null value are ordered first
apply group by so i reduce to a single contact record and if addresses with a district are retained
apply where clause to remove addresses with at least one district value
It works -- it's just a bit ugly.
You could try this, use LEFT JOIN and count the related record which is zero.
SELECT mr.contact_id
FROM recipients mr
LEFT JOIN address a ON mr.contact_id = a.contact_id
LEFT JOIN district_values di ON a.id = di.entity_id
WHERE mr.mid = 29
GROUP BY mr.contact_id
HAVING COUNT(a.*) = 0 AND COUNT(di.*) = 0
In my thread based messaging system, the table schema is
> messages table
id(int auto incr primary key)
body(varchar)
time(datetime)
>message_reference table
id(int auto incr primary key)
message_id(forgain key from message table)
sender
receiver
Here, I want to select the first message id which is sent to a new receiver and sender is the user who is logged in.
Doing this with multiple queries and some code is obviously possible but can it be done with a single query for performance issues??
You can try
EDIT:
If the id is auto increment, then the id will also increase with time and you can use:
SELECT message_reference.message_id, message_reference.receiver, messages.body
FROM message_reference, messages
WHERE message_reference.message_id IN (SELECT MIN(message_reference.message_id)
FROM message_reference
GROUP BY message_reference.receiver)
AND message_reference.message_id = messages.id AND message_reference.sender = <sender>
Here's my best guess as to what you want, but it would be easier if you gave known inputs, example data, and expected output.
SELECT
MR2.message_id
FROM (
SELECT
MR.sender,
MR.receiver,
M.MIN(`time`) AS min_time
FROM
Message_References MR -- Either use plural names (my personal preference) or singular, but don't mix them
INNER JOIN Messages M ON
M.id = MR.message_id
WHERE
MR.sender = <sender>
GROUP BY
MR.received) SQ
INNER JOIN Message_References MR2 ON
MR2.sender = SQ.sender AND
MR2.receiver = SQ.receiver AND
MR2.`time` = SQ.min_time
select mr.message_id from
message_reference as mr inner join
(select mr1.reciever max(m1.time) as time from messages as m1
inner join message_reference as mr1 on mr1.message_id = m1.id
group by mr1.reciever) as last
on mr.reciever = last.reciever and mr.time = last.time
join message reference with "maxtime per reciever" table on reciever and time
Well I got the answer, Just a group by query worked the way I wanted. I used query
SELECT SENDER,
RECEIVER,
BODY,
TIME,
MESSAGE_ID
FROM MESSAGE_REF JOIN MESSAGE
ON MESSAGE.ID=MESSAGE_REF.MESSAGE_ID
ORDER BY 'TIME' GROUP BY RECEIVER`
Thanks everyone for the help.