MySQL query to select only entries where value = X and value <> Y - mysql

Raw MySQL queries are absolutely not my forte, so I'm struggling with this a bit, but: with a straightforward table layout like this:
+----+-----------+----------+---------------------+
| id | status_id | order_id | created_at |
+----+-----------+----------+---------------------+
| 1 | 1 | 1 | 2016-03-21 20:40:39 |
| 2 | 3 | 1 | 2016-03-21 20:40:45 |
| 3 | 5 | 1 | 2016-03-21 20:47:14 |
| 4 | 1 | 2 | 2016-03-25 12:14:44 |
| 6 | 3 | 2 | 2016-03-25 12:16:12 |
| 7 | 5 | 2 | 2016-03-25 12:47:43 |
| 8 | 1 | 3 | 2016-03-26 17:25:12 |
| 9 | 3 | 3 | 2016-03-26 17:25:48 |
+----+-----------+----------+---------------------+
I want to select only the order_id rows where the status_id equals 3, but not where that same order_id has a status_id of 5. As a result, my query should only return order ID 3, but my current query returns all 3 order IDs in the results:
$statusQueryString = 'SELECT DISTINCT order_id
FROM shop_order_status_log_records
WHERE status_id = 3 AND status_id <> 5 ORDER BY created_at';
Where am I going wrong with my query?

Use post aggregate filtering when you need 2 or more conditions per group.A simple rule WHERE filters rows HAVING filters groups
SELECT order_id FROM shop_order_status_log_records
GROUP BY order_id
HAVING SUM(status_id = 3)>0
AND SUM(status_id = 5)=0

Related

Selecting rows that are not defined

How to select rows which are not defined? Like row 2 have undefined day 3 and row 3 have undefined day 1. I want them to be 0 in result set.
+----+-----+-------+
| id | day | count |
+----+-----+-------+
| 1 | 1 | 262 |
| 1 | 2 | 685 |
| 1 | 3 | 984 |
| 2 | 1 | 692 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 3 | 3 | 741 |
+----+-----+-------+
EDIT:
I want select count from days 1, 2 and 3 (not whole table) and display 0 on undefined day.
We can get all unique id values in a Derived Table.
For day, you seem to want only 1,2 and 3 only. So we can directly consider these values only using UNION ALL.
CROSS JOIN between them to get all possible combinations.
LEFT JOIN from all_combinations table to the main table on id and day.
We can use Coalesce() function to consider 0 value for count, for the cases where there is no matching row in the main table
Try the following:
SELECT all_combinations.id,
all_combinations.day,
COALESCE(t.count, 0) AS count
FROM
(
SELECT ids.id, days.day
FROM
(SELECT DISTINCT id FROM your_table) AS ids
CROSS JOIN
(SELECT 1 AS day UNION ALL SELECT 2 UNION ALL SELECT 3) AS days
) AS all_combinations
LEFT JOIN your_table AS t
ON t.id = all_combinations.id AND
t.day = all_combinations.day
Result:
| id | day | count |
| --- | --- | ----- |
| 1 | 1 | 262 |
| 2 | 1 | 692 |
| 3 | 1 | 0 |
| 1 | 2 | 685 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 1 | 3 | 984 |
| 2 | 3 | 0 |
| 3 | 3 | 741 |
View on DB Fiddle

Update records based on date on MySQL using joins

I have all those tables above.
car_model_tbl
-----------------------------
id | car_model_name|status |
-----------------------------
1 | seria_1 | 1 |
-----------------------------
2 | golf_4 | 1 |
-----------------------------
3 | C_Class | 1 |
-----------------------------
4 | golf_5 | 1 |
-----------------------------
5 | seria_2 | 0 |
-----------------------------
car_manufacturer_tbl
-------------------------
id |car_manufactu_name |
-------------------------
1 | bmw |
-------------------------
2 | volkswagen |
-------------------------
3 | mercedes |
-------------------------
car_service_tbl
---------------------------------
id | model_id| service_date |
---------------------------------
1 | 1 | 2018-03-10 |
---------------------------------
2 | 2 | 2018-02-10 |
---------------------------------
3 | 1 | 2018-01-10 |
---------------------------------
4 | 1 | 2017-12-10 |
---------------------------------
5 | 2 | 2017-12-10 |
---------------------------------
6 | 3 | 2018-02-10 |
---------------------------------
7 | 2 | 2018-01-10 |
---------------------------------
9 | 4 | 2018-03-10 |
---------------------------------
10 | 4 | 2018-02-10 |
---------------------------------
11 | 5 | 2018-02-10 |
---------------------------------
car_model_manufacturer_relation
-------------------------------------------------
id | model_id | manufactu_id| service_status |
-------------------------------------------------
1 | 1 | 1 | 1 |
-------------------------------------------------
2 | 5 | 1 | 1 |
-------------------------------------------------
3 | 2 | 2 | 1 |
-------------------------------------------------
4 | 4 | 1 | 1 |
-------------------------------------------------
5 | 2 | 2 | 1 |
-------------------------------------------------
6 | 3 | 3 | 1 |
-------------------------------------------------
I need to update car_model_manufacturer_relation.service_status = '0'
where car_service_tbl.service_date < "2018-03-01".
In this case car_model_manufacturer_relation.service_status of models 2, 3 and 5 should be set to '0' because every car_service_tbl.service_date for these models is smaller than "2018-03-01".
However, for models 1 and 4 car_model_manufacturer_relation.service_status should stay '1' because even that they have records smaller than "2018-03-01" they also have bigger dates ex. "2018-03-10".
I am trying to create a query for this but until now without success.
You'll need to nest a grouped query, to get the MAX date per model, and update from that.
update car_model_manufacturer_relation as cmmr,
(select model_id, max(service_date) as check_date
from car_service_tbl
group by model_id) as cst
set cmmr.service_status = '0'
where cmmr.model_id = cst.model_id
and cst.check_date < "2018-03-01"
Where you're using more than one table and the table names include underscores, I try and alias the tables to make the code a little shorter and easier on the eye, hence the use of cmmr and cst as table aliases.
The MAX date has also been renamed for clarity as check_date. You can of course name this anything you wish.
With sub query:
UPDATE car_model_manufacturer_relation c
LEFT join (SELECT model_id, service_date FROM car_service_tbl ORDER BY service_date DESC LIMIT 1) as s ON s.model_id = c.model_id
SET service_status=0
WHERE c.service_date < "2018-03-01"
#tyro - be careful with your solution, as a LEFT JOIN would update the service status to 0 when there wasn't a service date within the car_service_tbl. You would need to use a full join, rather than just the LEFT JOIN as you suggested in order to update the records correctly I feel.

SQL query that randoms the id from all posible ids in table and outputs the rows containing that id

I want a query that selects all rows that have the UploadedbyUserID = Rand() (selects random id from possible UploadbyUserID in this case 4, 3 and 22 and only those 3 not 2 nor 5)
And if the rand gives 4 it outputs this:
+------+------+------------+--------------------+
| id | name | date | UploadedbyUserID |
+------+------+------------+--------------------+
| 1 | 2222 | Testing | 4 |
| 2 | Jack | description| 4 |
| 6 | Zara | 2007-02-06 | 4 |
+------+------+------------+--------------------+
This is the whole table
+------+------+------------+--------------------+
| id | name | date | UploadedbyUserID |
+------+------+------------+--------------------+
| 1 | 2222 | Testing | 4 |
| 2 | Jack | description| 4 |
| 3 | ffdsd| 2007-05-06 | 4 |
| 4 | dsm | 2007-05-27 | 3 |
| 5 | dddd | 2007-04-06 | 3 |
| 6 | Zara | 2007-02-06 | 4 |
| 7 | John | 2007-01-24 | 22 |
+------+------+------------+--------------------+
and if it randomizes 3 it outputs this
+------+------+------------+--------------------+
| id | name | date | UploadedbyUserID |
+------+------+------------+--------------------+
| 4 | dsm | 2007-05-27 | 3 |
| 5 | dddd | 2007-04-06 | 3 |
+------+------+------------+--------------------+
Ask if you need more information
Hmmm. This is one way:
select t.*
from (select uploadedbyuserid
from t
order by rand()
limit 1
) u join
t
using (uploadedbyuserid);
First, let me say that this is weighted by the number of times that a user has uploaded something. So, user "4" would appear a bit more often than "3", in your example. If this is an issue:
select t.*
from (select uploadedbyuserid
from (select distinct uploadedbyuserid from t) t
order by rand()
limit 1
) u join
t
using (uploadedbyuserid);
The next observation is that this can be compute intensive. If you have lots of rows, there are various ways to speed these up. For instance, one simple method would be to get about 1 out of 10000 rows:
select t.*
from (select uploadedbyuserid
from (select distinct uploadedbyuserid
from t
) t
where rand() < 0.001
order by rand()
limit 1
) u join
t
using (uploadedbyuserid);

mysql: order -> limit -> sum... possible?

i am loosing it over the following problem:
i have a table with participants and points. each participant can have up to 11 point entries of which i only want the sum of the top 6.
in this example lets say we want the top 2 of 3
+----+---------------+--------+
| id | participantid | points |
+----+---------------+--------+
| 1 | 1 | 11 |
+----+---------------+--------+
| 2 | 3 | 1 |
+----+---------------+--------+
| 3 | 3 | 4 |
+----+---------------+--------+
| 4 | 2 | 3 |
+----+---------------+--------+
| 5 | 1 | 5 |
+----+---------------+--------+
| 6 | 2 | 10 |
+----+---------------+--------+
| 7 | 2 | 9 |
+----+---------------+--------+
| 8 | 1 | 3 |
+----+---------------+--------+
| 9 | 3 | 4 |
+----+---------------+--------+
as a result i want something like
+---------------+--------+
| participantid | points |
+---------------+--------+
| 2 | 19 |
+---------------+--------+
| 1 | 16 |
+---------------+--------+
| 3 | 8 |
+---------------+--------+
(it should be ordered DESC by the resulting points)
is this at all possible with mysql? in one query?
oh and the resulting participant ids should be resolved into the real names from another 'partcipant' table where
+----+------+
| id | name |
+----+------+
| 1 | what |
+----+------+
| 2 | ev |
+----+------+
| 3 | er |
+----+------+
but that should be doable with a join at some point... i know...
Using one of the answers from ROW_NUMBER() in MySQL for row counts, and then modifying to get the top.
SELECT ParticipantId, SUM(Points)
FROM
(
SELECT a.participantid, a.points, a.id, count(*) as row_number
FROM scores a
JOIN scores b ON a.participantid = b.participantid AND cast(concat(a.points,'.', a.id) as decimal) <= cast(concat(b.points,'.', b.id) as decimal)
GROUP BY a.participantid, a.points, a.id
) C
WHERE row_number IN (1,2)
GROUP BY ParticipantId
Had an issue with ties until I arbitrarily broke them with the id

SQL, difficult fetching data query

Suppose I have such a table:
+-----+---------+-------+
| ID | TIME | DAY |
+-----+---------+-------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 1 | 1 | 2 |
| 2 | 2 | 2 |
| 3 | 3 | 2 |
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 3 | 3 |
| 1 | 1 | 4 |
| 2 | 2 | 4 |
| 3 | 3 | 4 |
| 1 | 1 | 5 |
| 2 | 2 | 5 |
| 3 | 3 | 5 |
+-----+---------+-------+
I want to fetch a table which represents 2 IDs which got the largest sum of TIME within the last 3 days (means from 3 to 5 in a DAY column)
So the correct result would be:
+-----+---------+
| ID | SUM |
+-----+---------+
| 3 | 9 |
| 2 | 6 |
+-----+---------+
The original table is much larger and more complex. So i need a generic approach.
Thanks in advance.
And so I just learned that MySQL used LIMIT instead of TOP...
fiddle
CREATE TABLE tbl (ID INT,tm INT,dy INT);
INSERT INTO tbl (id, tm, dy) VALUES
(1,1,1)
,(2,2,1)
,(3,3,1)
,(1,1,2)
,(1,1,1)
SELECT ID
,SUM(SumTimeForDay) SumTimeFromLastThreeDays
FROM (SELECT ID
,SUM(tm) SumTimeForDay
FROM tbl
GROUP BY ID, dy
HAVING dy > MAX(dy) -3) a
GROUP BY id
ORDER BY SUM(SumTimeForDay) DESC
LIMIT 2
select t1.`id`, sum(t1.`time`) as `sum`
from `table` t1
inner join ( select distinct `day` from `table` order by `day` desc limit 3 ) t2
on t2.`da`y = t1.`day`
group by t1.`id`
order by sum(t1.`time`) desc
limit 2