write single SQLquery if possible? - mysql

There are three tables (column names are in brackets):
1-user_table (user_id, social_site)
value in social_site -> fb, whatsapp, wechat
2-booking_confirm (booking_id)
3-payment_table (order_id, payment_status)
payment_status = "success" or "fail"
no of user | social site | no of payment successful
34 | fb | 10
"no of user": find count(user_id) with respect to "fb" and "payment" (when order_id = booking_id and payment_status="success")

There's no connection between USER_TABLE (your first table) and another two tables, so the following query certainly won't work, but might give you an idea of how to do it (once you manage to fix what's wrong).
select count(*)
from user_table u join ??? on ??? --> what goes here?
--
booking_confirm b join payment_table p on b.booking_id = p.order_id
where u.social_site = 'fb'
and p.payment_status = 'success'

Related

Error on using outer query column in subquery for date constraint

We have a Mariadb table with users details in (users)
We have a 2nd table for review dates (reviewdates)
| reviewID |USERID |A review date |
| 001 | 123 |2017-01-08 09:02:10 |
etc...
That records review meeting dates against each user.
We have a 3rd table (userdata) with multiple types of user data in. Field id 101 is new targets for the review. Field id 98 is old targets from the previous review.
|dataID|Userid |Field ID |FieldValue |UpdatedOn |UpdatedBy|
-------------------------------------------------------------
|0001 |123 | 101 |my new target|2017-01-10|145 |
|0002 |123 | 98 |my old target|2017-01-10|0 |
New Target (field ID 101) gets copied to old targets (field id 98) when the review is completed.
Either field can be updated at any time.
Each user has many review dates. I need to compare the first value of the old field after the review is complete with the last value before the review date to make sure they have copied over correctly. As users can change either field it has to a comparison of immediately before and after the completion process.
so I join users and reviewdates
select users.userid,users.username,reviewdates.meetingdate
from companyusers users
join reviewdates on reviewdates.userid = users.userid
and this gives me all the review dates for all users
I then tried to find the most recent entry for the 101 field :
select users.userid,users.username, reviewdates.meetingdate, latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101)
) as latest101 on (latest101.userid = users.userid)
and this works OK too but when I try to find the most recent entry before each review date:
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < reviewdates.meetingdate)
) as latest101 on (latest101.userid = users.userid)
I get an
"unknown column reviewdates.meetingdate in where clause"
error. I've found loads of statements saying I can't refer to an outer join in a subquery but none that provide possible answers that apply to these date constraints.
Any help or pointers would be appreciated.
I do not see that you are filtering the most outer 'reveiwdates' table with anything specific, it is just used to display the 'meetingdate' based on the inner queries,
in that case firstly, there is no reason to use the same reference inside subquery.
Secondly, there are so many 'meetingdates', which specific meeting date are we comparing against the 'updatedOn' ?
You cannot join two tables on an in-equality condition.
Either a constant review date filter needs be applied or a procedure needs be written to loop through each meeting date for a user in the 'reviewdates' table.
If you just care about the latest review for that user then you could fetch and compare the latest 'reviewdate' just like how you are comparing the latest updated on, 'updatedOn' does not seem to have time component and to avoid other issues use date() or equivalent while comparing.
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < (select date(max(reviewdates.meetingdate)) from reviewdates where u2.userid = reviewdates.userid))
) as latest101 on (latest101.userid = users.userid)

Friends list and unread messages SQL query giving unexpected result

I have all day trying to get a result of a SQL query but does not give me the expected result.
My tables at which I consult are:
tcc_friends
id(PK AUTO_INCREMENT)
user_from (FK tcc_user (nickname) )
user_to (FK tcc_user (nickname) )
tcc_messages
id (PK AUTO_INCREMENT)
message
reg_time
wasRead
id_room (FK tcc_friends(id))
test records that have currently inserted are:
tcc_friends
id_room user_from user_to
5 hu example#gmail.com
6 hu example222222#hotmail.com
tcc_messages
id message id_room
1 a 5
2 b 5
3 c 3
SQL:
select
u.*,
f.id_room,COUNT(m.id) as newMessages
from
tcc_friends f,
tcc_user u,
tcc_messages m
where
u.nickname = 'hu' IN (u.nickname = f.user_from and f.user_to='hu') or
(u.nickname = f.user_to and f.user_from='hu') AND
(m.id_room = f.id_room and m.wasRead = 0)
GROUP BY
u.nickname;
RESULT:
id nickname id_room newMessages
81 example#gmail.com 5 2
I'm trying to get a user's friends and also add unread messages but displays only friends who have a message and I'd like to show all friends whether or not unread
Can anybody help me? Regards and Thank you all
First, don't use SELECT *. It's a horrible practice and you should get out of the habit of doing it as quickly as possible.
Second, learn how to use explicit JOINs. Don't list all of your tables in the WHERE clause. That's syntax that has been obsolete (for good reason) for 20 years. That's also what's causing your problem here because all of your joins are INNER JOINs by default.
Try something like this instead:
SELECT
U.id,
U.nickname,
F.id_room,
COUNT(M.id) as newMessages
FROM
tcc_Friends F
INNER JOIN tcc_User U ON U.nickname = F.user_from
LEFT OUTER JOIN tcc_Messages M ON
M.id_room = F.id_room AND
M.wasRead = 0 -- Are you using camelcase or underscores in column names?? Make up your mind and stick to it.
WHERE
F.user_to = 'hu'
GROUP BY
U.nickname;
That isn't going to get you all the way there, because I'm unclear on what exactly you're trying to get as far as number of unread messages - is it unread messages for the user at all? How does example#gmail.com have 2?
Remove the and m.wasRead = 0 from m.id_room = f.id_room and m.wasRead = 0 in the WHERE clause. By this query you get friends only with messages that have wasRead property 0. By doing above change you will get all the friends and 0 as the count if they don't have any new messages.

MySQL query to convert normalized data into single row

I'm trying to create a query to format content from normalized tables into a single row.
What I would like is something like this with a single row for each contact:
Name Mobile Office Home
--------------- ----------- ---------- ----------
Fred Flintstone 123-456-7890 234-567-8901 789-012-3456
Barney Rubble 456-789-0123 678-901-2345
Wilma Flintstone 567-890-1234 789-012-3456
What I am getting from my latest query is this, with multiple rows per contact:
Name Phone Phone_Type
--------------- ------------ -----------
Fred Flintstone 123-456-7890 Mobile
Fred Flintstone 234-567-8901 Office
Fred Flintstone 789-012-3456 Home
Barney Rubble 456-789-0123 Mobile
Barney Rubble 678-901-2345 Home
Wilma Flintstone 567-890-1234 Mobile
Wilma Flintstone 789-012-3456 Home
Here are the tables involved (simplified):
contacts
----------
contact_id
name
link_contact_phonenumbers
-------------------------
contact_id
phone_number_id
phone_numbers
-------------
phone_number_id
phone_number
type_id
ref_phone_types
---------------
type_id
name
Here is what I have so far:
SELECT
cn.name as Name,
concat( left(ph.phone_number,3) , "-" , mid(ph.phone_number,4,3) , "-", right(ph.phone_number,4)) as Phone,
pt.name as Phone_Type
FROM
contacts cn
LEFT JOIN link_contact_phonenumbers lp ON lp.contact_id = cn.contact_id
LEFT JOIN phone_numbers ph ON ph.phone_number_id = lp.phone_number_id
LEFT JOIN ref_phone_types pt ON pt.type_id = ph.type_id
I looked at using GROUP_CONCAT() function, but that pulls all the content into a single column. I need them to go into their own columns.
I've been looking into subqueries combined with IF() but haven't figured it out yet.
Is there a way to do this in pure SQL?
It sounds like you can accomplish what you are looking for with a few joins:
Select a.name, c.phone_number, d.phone_number, e.phone_number from contacts a
left join link_contact_phonenumbers b on a.contact_id = b.contact_id
left join phone_numbers c on b.phone_number_id = c.phone_number_id and c.type_id = "whatever id is mobile"
left join phone_numbers d on b.phone_number_id = d.phone_number_id and d.type_id = "whatever id is office"
left join phone_numbers e on b.phone_number_id = e.phone_number_id and e.type_id = "whatever id is home"
I do not know if this is the most efficient way, I also don't have a database to test with right now so it might be off, but it should point you in the right direction. Also might have to group by a.name in case the first join adds multiple rows.
Here is one solution for others that will find this in the future:
SELECT
cn.name as Name,
MAX(CASE WHEN pt.type_id = '1' THEN ph.phone_number ELSE NULL END) as Mobile,
MAX(CASE WHEN pt.type_id = '2' THEN ph.phone_number ELSE NULL END) as Office,
MAX(CASE WHEN pt.type_id = '3' THEN ph.phone_number ELSE NULL END) as Home
FROM
contacts cn
LEFT JOIN link_contact_phonenumbers lp ON lp.contact_id = cn.contact_id
LEFT JOIN phone_numbers ph ON ph.phone_number_id = lp.phone_number_id
LEFT JOIN ref_phone_types pt ON pt.type_id = ph.type_id
GROUP BY cn.contact_id
Found this solution here: How to Denormalize a Normalized Table Using SQL
I'm not sure if this is the most efficient, but it works.

MYSql search perfomance problems

account
act_id | act_name | grp_id | grp_id_2
2 | test | 4 | 10
promotion
pml_id | act_id | grp_id
2 | 2 | null
3 | null | 4
4 | null | 10
I have two tables, shown above (trimmed down). Account has about 15000 records, promotion about 20000.
Customer basically wants it so that they could search for an account name, say 'test'. And it would show the promotions 2, 3 and 4.
Promotion 3 would show because the account 'test' has grp_id = 4.
Promotion 4 would show because account 'test' has grp_id_2 = 10.
I originally did this with a couple of joins
SELECT pml_id FROM promotion
LEFT JOIN account AS account1 ON promotion.act_id = account1.act_id
LEFT JOIN account AS account2 ON promotion.grp_id = account2.grp_id
LEFT JOIN account AS account3 ON promotion.grp_id = account3.grp_id_2
WHERE account1.act_name LIKE 'test%' or account2.act_name LIKE 'test%'
or account3.act_name LIKE 'test%'
GROUP BY pml_id
The problem with this is this ended up taking a long time when I started having to join 5 times to the account table. It also gave me about 10000000 records (without the group by). Most of the promotions use a grp_id, rarely do they use an act_id.
Is there any way to search the act_name column quickly in this scenario? Without having to do so many joins?
I have single indexes on act_id, pml_id, grp_id, grp_id_2
Note: This is part of a query where the user may not search by account. I.E the where clause may not always be there
Use an INNER JOIN instead to avoid scanning the entire table :
SELECT p.pml_id
FROM account a
INNER JOIN promotion p
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_name LIKE "test%";
Is this any faster?
SELECT pml_id FROM promotion p
LEFT JOIN account a
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_name LIKE "test%";
Try this one as well, if you have an index on act_id, this should be quite a bit faster:
SELECT * FROM promotion p
LEFT JOIN account a
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_id IN (
SELECT act_id FROM account WHERE act_name LIKE "test%"
)

SQL: Series of joins

I am trying to find out how many comments there are in a question that has been made by the owner of the page (the admin).
tblQuestion: (question table)
queID
queUserID
queCompanyID (the owner of the page where questions are asked)
tblReplies: (where comments in the question are saved)
repQuestionID (queID = parent ID)
repUserID (the user ID who made the comment)
tblUsers: (where the users are stored)
uID (user ID, autoincrement)
uCompanyID (if this id is the same as a company id, the user is admin of that page)
So, I want to know how many reply posts are made by the company owner (a user with uCompanyID the reply posts' parent companyID - queCompanyID).
I tried doing this to get number of posts made by page admin, but does not seem to work:
SELECT COUNT(*) tblReplies.repID
FROM tblReplies
JOIN
tblQuestions ON tblQuestions.queID = tblReplies.repQuestionID
JOIN
tblUsers ON tblQuestions.queCompanyID = tblUsers.uCompanyID
WHERE tblQuestions.queID = 68 AND tblUsers.uCompanyID = 1
I really hope there is a ninja out there who can help me, I've spent hours and still nothing.
Thanks!
This should work (assuming there is a userID on the tblUsers table.:
SELECT
count(*),
tblReplies.repID
FROM
tblReplies,
tblQuestions,
tblUsers
WHERE
tblQuestions.queID = tblReplies.repQuestionID AND
tblQuestions.queCompanyID = tblUsers.uCompanyID AND
tblUser.uID = tblReplies.repUserID AND
tblUsers.uCompanyID = 1 AND
tblQuestions.queID = 68
GROUP BY tblReplies.repID
SELECT COUNT(*) AS total
FROM tblReplies
WHERE repQuestionID =68 AND repUserID =1
This should work.
SELECT count(*) as num_of_comments
FROM tblQuestion a
INNER JOIN tblReplies b
on a.queID = b.repQuestionID
INNER JOIN tblUsers c
on b.queCompanyID = c.uCompanyID
where a.queID = 68
and c.uCompanyID = 1