MySQL and/or query for advanced search between several tables - mysql

Now, I am using this query
SELECT h . * ,
(SELECT state
FROM house_name
WHERE name = h.house_name
AND purpose = h.purpose
AND district_id = (
SELECT id
FROM district
WHERE name = h.district )
LIMIT 1
) AS hstate,
(SELECT name
FROM district
WHERE id = (
SELECT district_id
FROM house_name
WHERE name = h.house_name
LIMIT 1 )
LIMIT 1
) AS origin_d,
(SELECT id
FROM house_name
WHERE name = h.house_name
AND purpose = h.purpose
AND district_id = (
SELECT id
FROM district
WHERE name = h.district )
LIMIT 1
) AS hnameid,
m.display_name
FROM house_detail h
LEFT JOIN members m ON m.id = h.member_id
WHERE
h.deleted =0
AND h.approve =0
AND
(SELECT state
FROM house_name
WHERE state = 'N'
AND name = h.house_name
AND purpose = h.purpose
AND district_id = (
SELECT id
FROM district
WHERE
name = h.district )
LIMIT 1
) IS NOT NULL
AND h.price <=1000000
OR h.price >=70000000
OR (
h.purpose = 'house'
AND h.rent >=100000
)
OR (
h.purpose = 'industry'
AND h.rent >=700000
)
OR h.rent <=5000
On the Where clause:
The h.approve can be 0 or 1 or 2
The h.deleted can be 0 or 1
The state can be Y or N
What I want to do is:
The h.deleted , h.approve and the state MUST be 0, 0 and N respectively
The h.rent and h.price may under a specific range.
And the current problem is it will still select some data where the h.deleted , h.approve and state may be 1 or 2 , 1 and Y. I think it's because there're some OR between them.
Is there any way to output my expected result ?

Set bracket around the following statements:
WHERE
name = h.district )
LIMIT 1
) IS NOT NULL
AND (h.price <=1000000
OR h.price >=70000000
OR (
h.purpose = 'house'
AND h.rent >=100000
)
OR (
h.purpose = 'industry'
AND h.rent >=700000
)
OR h.rent <=5000)
opening ( after the last AND and closing ) at the end... that should do

Related

Joining Table with Itself MYSQL

I have a scenario where i have table app_users in which i am storing multiple types of users , and each user have different column for phone number , and Names , i am trying to get phone number and name of specific type of user , it's hard to understand this way , but see the working example Query below :
SET #sk := (SELECT app_users.skeeper_phone as sphone FROM app_users WHERE app_users. app_user_type = 'SKEEPER' LIMIT 1);
SELECT
acs.id as complainId,
IF(
(#sk) > 0,
#sk,
''
) as sk_phone,
(
SELECT
IF(
auu.institute_phone IS NULL,
auu.department_phone,
auu.institute_phone
) as cphone
FROM app_users as auu
WHERE auu.id = acs.app_customer_id
LIMIT 1
) as cphone,
(
SELECT auuu.other_user_phone as ephone
FROM app_users as auuu,app_admin_assign_eng as aase
WHERE aase.app_complain_service_id = acs.id AND auuu.id = aase.engineer_id
LIMIT 1
) as ephone,
(
SELECT auuu.fullname as ename
FROM app_users as auuu,app_admin_assign_eng as aase
WHERE aase.app_complain_service_id = acs.id AND auuu.id = aase.engineer_id
LIMIT 1
) as ename,
(
SELECT auuu.other_user_phone as ephone
FROM app_users as auuu,app_admin_assign_eng as aase
WHERE aase.app_complain_service_id = acs.id AND auuu.id = aase.admin_id
LIMIT 1
) as aphone
FROM app_complain_services as acs,app_users as au
WHERE
acs.id = 5
GROUP BY acs.id
Above query is working alright , but it is not optimized , what could be optimization and other ways to solve similar problem?

Operand should contain 1 column(s) - in mysql query

I have the following complex query that is giving me an error
Operand should contain 1 column(s)
Can anyone suggest what is wrong
SELECT
t.user_id AS user_id,
t.organisation_id AS organisation_id,
t.firstname AS firstname,
t.surname AS surname,
t.username AS username,
t.year_id AS year_id,
t.form_name AS form_name,
t.House AS House,
rcPoints.total AS milestoneRedeemedCodesTotal,
rcFilteredPoints.total AS redeemedCodesTotalFiltered,
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) AS redeemedCodesTotalAvailableFiltered,
ABS(
FLOOR(
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) / 1000
) * 1000
) AS redeemedCodesTotalTowardsMilestone,
ABS(
FLOOR(
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) / 1000
)
) AS redeemedCodesMilestoneTriggers,
COALESCE (milestoneHistory.total, 0) AS historyTotal
FROM
`myuser` `t`
LEFT JOIN (
SELECT
rc.user_id AS user_id,
SUM(rc.school_points) AS total
FROM
`redeemed_codes` `rc`
INNER JOIN myuser m ON (m.user_id = rc.user_id)
WHERE
(rc.date_redeemed >= 0)
AND (m.organisation_id = 58022)
GROUP BY
rc.user_id
) AS rcPoints ON (rcPoints.user_id = t.user_id)
LEFT JOIN (
SELECT
rc.user_id AS user_id,
SUM(rc.school_points) AS total
FROM
`redeemed_codes` `rc`
INNER JOIN myuser m ON (m.user_id = rc.user_id)
WHERE
(rc.date_redeemed >= 0)
AND (m.organisation_id = 58022)
GROUP BY
rc.user_id
) AS rcFilteredPoints ON (
rcFilteredPoints.user_id = t.user_id
)
LEFT JOIN (
SELECT
mh.user_id AS user_id,
mh.milestone_id AS milestone_id,
MAX(mh.points_when_triggered) AS total
FROM
`milestone_history` `mh`
WHERE
mh.milestone_id = 13
GROUP BY
mh.user_id
) AS milestoneHistory ON (
milestoneHistory.user_id = t.user_id
)
WHERE
(
(
SELECT
COALESCE (count(*), 0)
FROM
milestone_history mha
WHERE
mha.milestone_id = 13
AND mha.user_id = t.user_id
) = 0
)
AND (t.organisation_id = 58022)
AND
(
SELECT * FROM
redeemed_codes t1
WHERE
organisation_id = 1
AND
(
SELECT
sum(school_points)
FROM
redeemed_codes t2
WHERE
t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
ORDER BY redeemed_code_id
LIMIT 1
)
GROUP BY
t.user_id
ORDER BY
redeemedCodesMilestoneTriggers DESC
LIMIT 1
Your query might have multiple errors, but this condition in the WHERE clause is definitely suspect and would lead to that error:
AND (SELECT *
FROM redeemed_codes t1
WHERE organisation_id = 1 AND
(SELECT sum(school_points)
FROM redeemed_codes t2
WHERE t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
ORDER BY redeemed_code_id
LIMIT 1
)
I have no idea what you are trying to do. Sometimes, the solution is simply EXISTS:
EXISTS (SELECT *
FROM redeemed_codes t1
WHERE organisation_id = 1 AND
(SELECT sum(school_points)
FROM redeemed_codes t2
WHERE t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
)

Tricky SQL select

I have a message table, this table contains the following columns :
id | fromUserId | toUserId | sentDate | readDate | message | subject | fromUserDeleted(bit) | toUserDeleted(bit)
Now I need to select first message within every conversionen no mather if its from or to the current user. What makes it more complex is that I need to exclude messages that are deleted by the current user, so if the message is from current user then the fromUserDeleted have to be 0 if the message is from another user then toUserDeleted needs to be 0.
I know that there already are a lot of examples on how to select first post within groups but this is a bit more complex. The current user might be in toUserId or in fromUserId and the same goes for the deleted column.
This is what I have tried :
SELECT
m1.*,
(SELECT count(*) FROM mail m3 WHERE m3.fromUserId=m1.fromUserId AND m3.toUserId=m1.toUserId AND m3.toUserDeleted = 0) as messageCount,
(SELECT count(*) FROM mail m4 WHERE m4.toUserId=15 AND m4.readDate is null AND m4.toUserDeleted=0) as messagesNotRead
FROM
mail m1
LEFT JOIN mail m2 ON
((m1.fromUserId = m2.fromUserId) and
m2.)
WHERE
m2.id IS NULL AND
(m1.toUserId = 15 OR m1.fromUserId = 15)
ORDER BY
m1.sentDate DESC;
And :
SELECT
*
FROM
mail m1
WHERE
((m1.fromUserId = 15 AND m1.fromUserDeleted=0) OR (m1.toUserId = 15 AND m1.toUserDeleted=0)) AND
m1.id = (SELECT m2.id FROM mail m2 WHERE m2.fromUserId = 15 OR m2.toUserId = 15 OR m2.fromUserId = m1.fromUserId OR m2.fromUserId = m1.toUserId OR m2.toUserId = m1.fromUserId OR m2.toUserId = m1.toUserId order By m2.sentDate desc limit 0,1)
Order by m1.sentDate DESC
None of these will do what I need. Pleas help!
It´s okay if a stored procedure needs to be created.
IMPORTANT : This i MySQL
EDIT 1:
Pleas see this for example : http://sqlfiddle.com/#!2/6a2ef/3
DOING UNPIVOT to get fromUSerId, toUserId under same column so that we can pick correct userId based on the sentDate.
SELECT T.*
FROM
(
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY userVal ORDER BY sentDate DESC) as seq
FROM
(SELECT
*
FROM msg
WHERE (fromUserId = #userID AND fromUserDeleted = 0 )
OR (toUserId = #UserID AND toUserDeleted = 0)
)p
unpivot
( userVal for UserId in ( fromUserId, toUserId)
)as unpvt
) T
WHERE seq =1
Thanks for the feedback. Hopefully this will address your need:
DECLARE #UserID INT
SELECT #UserID = 15
SELECT *
FROM (
SELECT UserID, ConversationWithUserID, sentDate, readDate, [message], [subject], ROW_NUMBER() OVER (PARTITION BY ConversationWithUserID ORDER BY sentDate ASC) AS MessageOrder
FROM (
SELECT id, fromUserId AS UserID, toUserId AS ConversationWithUserID, sentDate, readDate, [message], [subject]
FROM dbo.mail
WHERE fromUserID = #UserID AND fromUserDeleted = 0
UNION
SELECT id, toUserId AS UserID, fromUserId AS ConversationWithUserID, sentDate, readDate, [message], [subject]
FROM dbo.mail
WHERE toUserId = #UserID AND toUserDeleted = 0
) x
) y
WHERE MessageOrder = 1
ORDER BY y.UserID, y.ConversationWithUserID

SQL query joining a few tables (MySQL)

I need a "little" help with an SQL query (MySQL).
I have the following tables:
COURIERS table:
+------------+
| COURIER_ID |
+------------+
DELIVERIES table:
+-------------+------------+------------+
| DELIVERY_ID | COURIER_ID | START_DATE |
+-------------+------------+------------+
ORDERS table:
+----------+-------------+-------------+
| ORDER_ID | DELIVERY_ID | FINISH_DATE |
+----------+-------------+-------------+
COORDINATES table:
+-------------+-----+-----+------+
| DELIVERY_ID | LAT | LNG | DATE |
+-------------+-----+-----+------+
In the real database I have more columns in each table, but for this example the above columns are enough.
What do I need?
An SQL query that returns all couriers [COURIER_ID], their last
delivery [DELIVERY_ID] (based on last START_DATE), the
delivery's last coordinate [LAT and LNG] (based on last DATE) and the remaining orders count (total of orders of the last delivery that have no FINISH_DATE).
A courier can have no deliveries, in this case I want DELIVERY_ID =
NULL, LAT = NULL and LNG = NULL in the result.
A delivery can have no coordinates, in this case I want LAT = NULL
and LNG = NULL in the result.
What was I able to do?
SELECT c.`COURIER_ID`,
d.`DELIVERY_ID`,
r.`LAT`,
r.`LNG`,
(SELECT COUNT(DISTINCT `ORDER_ID`)
FROM `ORDERS`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`
AND `FINISH_DATE` IS NULL) AS REMAINING_ORDERS
FROM `COURIERS` AS c
LEFT JOIN `DELIVERIES` AS d USING (`COURIER_ID`)
LEFT JOIN `COORDINATES` AS r ON r.`DELIVERY_ID` = d.`DELIVERY_ID`
WHERE (CASE WHEN
(SELECT MAX(`START_DATE`)
FROM `DELIVERIES`
WHERE `COURIER_ID` = c.`COURIER_ID`) IS NULL THEN d.`START_DATE` IS NULL ELSE d.`START_DATE` =
(SELECT MAX(`START_DATE`)
FROM `DELIVERIES`
WHERE `COURIER_ID` = c.`COURIER_ID`) END)
AND (CASE WHEN
(SELECT MAX(`DATE`)
FROM `COORDINATES`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) IS NULL THEN r.`DATE` IS NULL ELSE r.`DATE` =
(SELECT MAX(`DATE`)
FROM `COORDINATES`
WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) END)
GROUP BY c.`COURIER_ID`
ORDER BY d.`START_DATE` DESC
The problem is that this query is very slow (from 5 to 20 seconds) when I have over 5k COORDINATES and it does not returns all couriers sometimes.
Thank you so much for any solution.
Try this:
SELECT C.COURIER_ID, D.DELIVERY_ID, D.START_DATE, D.FINISH_DATE,
B.LAT, B.LNG, B.DATE, C.NoOfOrders
FROM COURIERS C
LEFT JOIN ( SELECT *
FROM (SELECT *
FROM DELIVERIES D
ORDER BY D.COURIER_ID, D.START_DATE DESC
) A
GROUP BY COURIER_ID
) AS A ON C.COURIER_ID = A.COURIER_ID
LEFT JOIN ( SELECT *
FROM (SELECT *
FROM COORDINATES CO
ORDER BY CO.DELIVERY_ID, CO.DATE DESC
) B
GROUP BY CO.DELIVERY_ID
) AS B ON A.DELIVERY_ID = B.DELIVERY_ID
LEFT JOIN ( SELECT O.DELIVERY_ID, COUNT(1) NoOfOrders
FROM ORDERS O WHERE FINISH_DATE IS NULL
GROUP BY O.DELIVERY_ID
) AS C ON A.DELIVERY_ID = C.DELIVERY_ID;
I haven't been able to test this query since I don't have a mysql database set up right now, much less with this schema and sample data. But I think this will work for you:
select
c.courier_id
, d.delivery_id
, co.lat
, co.lng
, oc.cnt as remaining_orders
from
couriers c
left join (
select
d.delivery_id
, d.courier_id
from
deliveries d
inner join (
select
d.delivery_id
, max(d.start_date) as start_date
from
deliveries d
group by
d.delivery_id
) dmax on dmax.delivery_id = d.delivery_id and dmax.start_date = d.start_date
) d on d.courier_id = c.courier_id
left join (
select
c.delivery_id
, c.lat
, c.lng
from
coordinates c
inner join (
select
c.delivery_id
, max(c.date) as date
from
coordinates c
group by
c.delivery_id
) cmax on cmax.delivery_id = c.delivery_id and cmax.date = c.date
) co on co.delivery_id = d.delivery_id
left join (
select
o.delivery_id
, count(o.order_id) as cnt
from
orders o
where
o.finish_date is null
group by
o.delivery_id
) oc on oc.delivery_id = d.delivery_id

SQL: Return specified column of max() row inside SELECT

I want to return the date column for each of the rows where max() is used within the SELECT. Or maybe there is a better way of doing this?
This is how I imagine it:
SELECT
MAX(time) as time, [date column from max(time) row] as timedate,
MAX(distance) as distance, [date column from max(distance) row] as distancedate,
MAX(weight) as weight, [date column from max(weight) row] as weightdate
Here is my current SQL, this does not return the date for each of the MAX() rows.
$db->query("SELECT e.id as id, e.name, MAX(ue.time) as time, MAX(ue.weight) as weight, MAX(ue.distance) as distance
FROM `users exercises` as ue
LEFT JOIN `exercises` as e ON exerciseid = e.id
GROUP BY e.id
LIMIT 30");
id | exerciseid | date | weight | distance | time
----------------------------------------------------------
1 | 1 | 2014-06-14 | 100 | 33 | null
2 | 1 | 2013-03-03 | 500 | 11 | null
3 | 1 | 2014-11-11 | null | null | 41
Current Output:
Array
(
[id] => 1
[name] => run
[time] => 41
[weight] => 500
[distance] => 33
)
Expected Output:
Array
(
[id] => 1
[name] => run
[time] => 41
[time_date] => 2014-11-11
[weight] => 500
[weight_date] => 2013-03-03
[distance] => 33
[distance_date] => 2014-06-14
)
SQL Fiddle: http://sqlfiddle.com/#!2/75e53/1
SELECT e.id as id, e.name,
MAX(ue.time) as time,
(
select date
from `users exercises`
WHERE time = MAX(ue.time) AND ue.`userid` = $userid
LIMIT 1
) as time_date,
MAX(ue.weight) as weight,
(
select date
from `users exercises`
WHERE weight = MAX(ue.weight) AND ue.`userid` = $userid
LIMIT 1
) as weight_date,
MAX(ue.distance) as distance,
(
select date
from `users exercises`
WHERE distance = MAX(ue.distance) AND ue.`userid` = $userid
LIMIT 1
) as distance_date
FROM `users exercises` as ue
LEFT JOIN `exercises` as e ON exerciseid = e.id
WHERE ue.`userid` = $userid
GROUP BY e.id
LIMIT 30
There's probably a more efficient way to do this, but sadly my MySQL skills aren't that good; however the code below does what you want:
Solution 1
select
mx.time
, t.date as timedate
, mx.distance
, d.date as distancedate
, mx.weight
, w.date as weightdate
from
(
SELECT
MAX(`time`) as `time`
, MAX(`distance`) as `distance`
, MAX(`weight`) as `weight`
from `users exercises`
) as mx
inner join `users exercises` as t on t.time = mx.time
inner join `users exercises` as d on d.distance = mx.distance
inner join `users exercises` as w on w.weight = mx.weight;
Solution 2
select
mx.time
, (select date from `users exercises` as x where x.time = mx.time limit 1) as timedate
, mx.distance
, (select date from `users exercises` as y where y.distance = mx.distance limit 1) as distancedate
, mx.weight
, (select date from `users exercises` as z where z.weight = mx.weight limit 1) as weightdate
from
(
SELECT
MAX(`time`) as `time`
, MAX(`distance`) as `distance`
, MAX(`weight`) as `weight`
from `users exercises`
) as mx;
For anyone using a db which support partition by there is a better way of implementing this; sadly MySQL does not support that functionality currently.
SQL Fiddle: http://sqlfiddle.com/#!2/75e53/13