MySQL: select list of battles, not shared between 2 users - mysql

I need to select list of "battle_id", which first "user_id" have, but not shared with second "user_id",
battles (table structure):
id user_id battle_id
0 1 44
1 1 55
2 1 66
3 2 44
4 2 77
5 3 88
6 3 99
7 4 44
8 4 55
9 4 66
sample of input and output:
example 1: input [user_id = 1, user_id = 2] output => 55,66
example 2: input [user_id = 1, user_id = 3] output => 44,55,66
example 3: input [user_id = 1, user_id = 4] output => null
thanks,

Sample data:
CREATE TABLE t
(`id` int, `user_id` int, `battle_id` int)
;
INSERT INTO t
(`id`, `user_id`, `battle_id`)
VALUES
(0, 1, 44),
(1, 1, 55),
(2, 1, 66),
(3, 2, 44),
(4, 2, 77),
(5, 3, 88),
(6, 3, 99),
(7, 4, 44),
(8, 4, 55),
(9, 4, 66)
;
Query:
select
battle_id
from
t
group by battle_id
having sum(user_id = 1) >= 1 and sum(user_id = 2) = 0
Result:
| battle_id |
|-----------|
| 55 |
| 66 |
Explanation:
You don't need to self join like suggested in comments. Do it in one go.
The user_id = 'whatever' inside the sum() function in having clause returns either true or false, meaning 1 or 0.
see it working live in an sqlfiddle.

You can use subqueries here. Somthing like that:-
SELECT battle_id
FROM battles
WHERE user_id IN ('Your first user_id', 'Your second user_id')
AND battle_id NOT IN (SELECT battle_id
FROM battles
WHERE user_id = 'Your second user_id');

Related

SQL - Get count of phone calls that resulted in meeting

I have a table named 'reports', something like this:
id
user_id
type
customer_id
text
created_at
1
1
1
3
....
2021-08-07 17:00:52
2
1
1
3
....
2021-08-12 10:11:11
3
1
1
9
....
2021-08-12 10:08:14
4
1
2
3
....
2021-08-12 10:04:08
5
1
2
9
....
2021-08-13 20:32:21
6
2
1
7
....
2021-08-13 20:34:17
7
2
2
8
....
2021-08-14 18:55:09
I want to get the count of rows that a user has submitted type 1 reports that result in a type 2 report.
Type 1 report means reporting a phone call to the customer and type 2 means meeting the customer. I want to receive the number of calls that resulted in an meeting.
For example, for user 1 should returns 3, because for customer 3, IDs 1 and 2 have led to ID 4, and for customer 9, ID 3 has led to ID 5.
But for user 2, ID 7 is type 2 but there is no previous type 1 report for customer 8, so it returns 0.
Schema (MySQL v5.7)
CREATE TABLE reports
(id int auto_increment primary key,
user_id int,
type int,
customer_id int,
text varchar(4),
created_at varchar(19)
);
INSERT INTO reports
(id, user_id, type, customer_id, text, created_at)
VALUES
(1, 1, 1, 3, '....', '2021-08-07 17:00:52'),
(2, 1, 1, 3, '....', '2021-08-12 10:11:11'),
(3, 1, 1, 9, '....', '2021-08-12 10:08:14'),
(4, 1, 2, 3, '....', '2021-08-12 10:04:08'),
(5, 1, 2, 9, '....', '2021-08-13 20:32:21'),
(6, 2, 1, 7, '....', '2021-08-13 20:34:17'),
(7, 2, 2, 8, '....', '2021-08-14 18:55:09');
Query #1
SELECT x.user_id
, COUNT(DISTINCT y.id) total
FROM reports x
LEFT
JOIN reports y
ON y.id<=x.id
AND y.user_id = x.user_id
AND y.customer_id = x.customer_id
AND y.type = 1
WHERE x.type = 2
GROUP
BY x.user_id;
user_id
total
1
3
2
0
View on DB Fiddle
You haven't shown how your are expecting results, but simply refer to User 1 and 2 and the total votes, you can try using a lateral join
select user_id, Coalesce(Sum(valid),0) rowcount
from reports r
join lateral (
select
case when exists (select * from reports rr
where rr.user_id=r.user_Id and rr.type=2 and rr.customer_id=r.customer_Id and r.type=1)
then 1 end valid
)x
group by User_Id
Example Fiddle
First you Count the number of customers that comply with your criterioa type and typoe 2. But ti get all user you need to join the list of users
I used LEFT join as also RIGHt JOIN in my example
CREATE TABLE reports
(`id` int, `user_id` int, `type` int, `customer_id` int, `text` varchar(4), `created_at` varchar(19))
;
INSERT INTO reports
(`id`, `user_id`, `type`, `customer_id`, `text`, `created_at`)
VALUES
(1, 1, 1, 3, '....', '2021-08-07 17:00:52'),
(2, 1, 1, 3, '....', '2021-08-12 10:11:11'),
(3, 1, 1, 9, '....', '2021-08-12 10:08:14'),
(4, 1, 2, 3, '....', '2021-08-12 10:04:08'),
(5, 1, 2, 9, '....', '2021-08-13 20:32:21'),
(6, 2, 1, 7, '....', '2021-08-13 20:34:17'),
(7, 2, 2, 8, '....', '2021-08-14 18:55:09')
;
SELECT IFNULL(Counts,0),t1.user_id
FROM (SELECT COUNT(distinct r.customer_id) counts,user_id
FROM reports r
WHERE type = 1 AND EXISTS(SELECT 1 FROM reports WHERE type = 2 AND customer_id = r.customer_id)
GROUP BY user_id) r RIGHT JOIN (SELECT DISTINCT user_id FROm reports) t1 ON r.user_id = t1.user_id
IFNULL(Counts,0) | user_id
---------------: | ------:
2 | 1
0 | 2
SELECT IFNULL(Counts,0),t1.user_id
FROM (SELECT DISTINCT user_id FROm reports) t1 LEFT JOIN (SELECT COUNT(distinct r.customer_id) counts,user_id
FROM reports r
WHERE type = 1 AND EXISTS(SELECT 1 FROM reports WHERE type = 2 AND customer_id = r.customer_id)
GROUP BY user_id) r ON r.user_id = t1.user_id
IFNULL(Counts,0) | user_id
---------------: | ------:
2 | 1
0 | 2
db<>fiddle here

Bug in MySQL query due to comparisons during joins

MySQL Server version: 5.5.41-0+wheezy1 - (Debian)
I've got a MySQL issue that is almost a year old. Have tried a few freelancers and while we got close, it was never totally solved.
I need to find the most recent outbound shipment for every product we have. And in the same row of results, the specific lot (including lot number and expiration date) of the product that was involved in that inventory adjustment.
The most recent version of this query is almost working but it has one fatal flaw (at least one). If the product only ever had one outbound shipment, it won't show up in the results. I'm assuming that is because on line 30 where a comparison is done, which looks like this
AND liai0.LocationInventoryAdjustmentItemID > liai.LocationInventoryAdjustmentItemID
I'm assuming it is because the one and only adjustment for a product couldn't be greater than itself, it fails to be considered by the where clause. I've tested this by adding a second, fake outbound shipment, for a missing product. The query then returns the missing product.
That isn't the only line with a comparison like that. Perhaps the same problem could happen if there was only ever one location for a given product (line 23) and perhaps if there was only ever one lot for a product (line 16).
Here is the latest version of the query:
SELECT
p.`ProductID`,
p.`ProductName`,
lot.`ProductLotID`,
lot.`ExpirationDate`,
lot.`LotNumber`,
lia.`LocationInventoryAdjustmentID`,
lia.`Created`,
liai.`count`
FROM products AS p
INNER JOIN product_lots AS lot ON lot.ProductID = p.ProductID
AND NOT EXISTS (
SELECT 1
FROM product_lots AS lot0
WHERE lot0.ProductID = lot.ProductID
AND lot0.ProductLotID > lot.ProductLotID
)
INNER JOIN product_locations AS pl ON pl.ProductLotID = lot.ProductLotID
AND NOT EXISTS (
SELECT 1
FROM product_locations AS pl0
WHERE pl0.ProductLotID = pl.ProductLotID
AND pl0.ProductLocationID > pl.ProductLocationID
)
INNER JOIN location_inventory_adjustment_items AS liai ON liai.ProductLocationID = pl.ProductLocationID
AND NOT EXISTS (
SELECT 1
FROM location_inventory_adjustment_items AS liai0
WHERE liai0.ProductLocationID = liai.ProductLocationID
AND liai0.LocationInventoryAdjustmentItemID > liai.LocationInventoryAdjustmentItemID
)
INNER JOIN location_inventory_adjustments AS lia ON lia.LocationInventoryAdjustmentID = liai.LocationInventoryAdjustmentID
INNER JOIN location_inventory_adjustment_reasons AS liar ON liar.ReasonID = lia.ReasonID
WHERE liar.`Name` LIKE '%Out-Bound%'
ORDER BY p.ProductID ASC, liai.LocationInventoryAdjustmentID
And now the tables
Table products
ProductID ProductName
1 Banana
2 Apple
3 Orange
4 Shirt
Table product_lots
ProductLotID ProductID ExpirationDate LotNumber
20 1 2022-01-01 Chikita22
21 3 2023-12-01 Florida-9
22 4 NULL HANES-001
23 1 2024-01-01 Chikita24
Table product_locations
ProductLocationID ProductLotID LocationCode LocationType
30 20 A-01-01-01 1
31 21 A-02-01-01 1
32 22 12-03-01-01 2
33 23 A-01-01-01 1
Table location_inventory_adjustment_items
LocationInventoryAdjustmentItemID LocationInventoryAdjustmentID ProductLocationID Count
40 50 30 100
41 51 31 200
42 52 32 300
43 53 33 150
Table location_inventory_adjustments
LocationInventoryAdjustmentID LocationTypeID ReasonID Created
50 1 4 2020-01-01
51 1 4 2020-05-01
52 2 4 2020-06-01
53 1 4 2020-07-07
Table location_inventory_adjustment_reasons
ReasonID Name Description
1 Discarded Product Trash
2 In-Bound Shipment Delivery
3 Out-Bound Shipment Product was shipped out
4 Out-Bound Shipment - FBA US Product was shipped out to an Amazon fulfillment center in the US
5 Out-Bound Shipment - FBA UK Product was shipped out to an Amazon fulfillment center in the UK
Now that the table structure is added, only the product Banana would be returned by the query since it is the only product to be involved in more than one inventory adjustment (more than one outbound shipment).
What can I do to fix this bug (in my query, obviously not a bug in MySQL)
Ican't really explain it it must be one or other restricted wording.
But try, at least mysql 8 workbench doesn't mind
CREATE TABLE products
(`ProductID` int, `ProductName` varchar(6))
;
INSERT INTO products
(`ProductID`, `ProductName`)
VALUES
(1, 'Banana'),
(2, 'Apple'),
(3, 'Orange'),
(4, 'Shirt')
;
✓
✓
CREATE TABLE product_lots
(`ProductLotID` int, `ProductID` int, `ExpirationDate` varchar(10), `LotNumber` varchar(9))
;
INSERT INTO product_lots
(`ProductLotID`, `ProductID`, `ExpirationDate`, `LotNumber`)
VALUES
(20, 1, '2022-01-01', 'Chikita22'),
(21, 3, '2023-12-01', 'Florida-9'),
(22, 4, NULL, 'HANES-001'),
(23, 1, '2024-01-01', 'Chikita24')
;
✓
✓
CREATE TABLE product_locations
(`ProductLocationID` int, `ProductLotID` int, `LocationCode` varchar(11), `LocationType` int)
;
INSERT INTO product_locations
(`ProductLocationID`, `ProductLotID`, `LocationCode`, `LocationType`)
VALUES
(30, 20, 'A-01-01-01', 1),
(31, 21, 'A-02-01-01', 1),
(32, 22, '12-03-01-01', 2),
(33, 23, 'A-01-01-01', 1)
;
✓
✓
CREATE TABLE location_inventory_adjustment_items
(`LocationInventoryAdjustmentItemID` int, `LocationInventoryAdjustmentID` int, `ProductLocationID` int, `Count` int)
;
INSERT INTO location_inventory_adjustment_items
(`LocationInventoryAdjustmentItemID`, `LocationInventoryAdjustmentID`, `ProductLocationID`, `Count`)
VALUES
(40, 50, 30, 100),
(41, 51, 31, 200),
(42, 52, 32, 300),
(43, 53, 33, 150)
;
✓
✓
CREATE TABLE location_inventory_adjustments
(`LocationInventoryAdjustmentID` int, `LocationTypeID` int, `ReasonID` int, `Created` date)
;
INSERT INTO location_inventory_adjustments
(`LocationInventoryAdjustmentID`, `LocationTypeID`, `ReasonID`, `Created`)
VALUES
(50, 1, 4, '2020-01-01'),
(51, 1, 4, '2020-05-01'),
(52, 2, 4, '2020-06-01'),
(53, 1, 4, '2020-07-07')
;
✓
✓
CREATE TABLE location_inventory_adjustment_reasons
(`ReasonID` int, `Name` varchar(27), `Description` varchar(65))
;
INSERT INTO location_inventory_adjustment_reasons
(`ReasonID`, `Name`, `Description`)
VALUES
(1, 'Discarded Product', 'Trash'),
(2, 'In-Bound Shipment', 'Delivery'),
(3, 'Out-Bound Shipment', 'Product was shipped out'),
(4, 'Out-Bound Shipment - FBA US', 'Product was shipped out to an Amazon fulfillment center in the US'),
(5, 'Out-Bound Shipment - FBA UK', 'Product was shipped out to an Amazon fulfillment center in the U')
;
✓
✓
SELECT
p.`ProductID`,
p.`ProductName`,
lot.`ProductLotID`,
lot.`ExpirationDate`,
lot.`LotNumber`,
lia.`LocationInventoryAdjustmentID`,
lia.`Created`,
liai.`count`
FROM products AS p
INNER JOIN product_lots AS lot ON lot.ProductID = p.ProductID
AND NOT EXISTS (
SELECT 1
FROM product_lots AS lot0
WHERE lot0.ProductID = lot.ProductID
AND lot0.ProductLotID > lot.ProductLotID
)
INNER JOIN product_locations AS pl ON pl.ProductLotID = lot.ProductLotID
AND NOT EXISTS (
SELECT 1
FROM product_locations AS pl0
WHERE pl0.`ProductLotID` = pl.`ProductLotID`
AND pl0.ProductLocationID > pl.ProductLocationID
)
INNER JOIN location_inventory_adjustment_items AS liai ON liai.ProductLocationID = pl.ProductLocationID
AND NOT EXISTS (
SELECT 1
FROM location_inventory_adjustment_items AS liai0
WHERE liai0.ProductLocationID = liai.ProductLocationID
AND liai0.`LocationInventoryAdjustmentItemID` > liai.`LocationInventoryAdjustmentItemID`
)
INNER JOIN location_inventory_adjustments AS lia ON lia.LocationInventoryAdjustmentID = liai.LocationInventoryAdjustmentID
INNER JOIN location_inventory_adjustment_reasons AS liar ON liar.ReasonID = lia.ReasonID
WHERE liar.`Name` LIKE '%Out-Bound%'
ORDER BY p.ProductID ASC, liai.LocationInventoryAdjustmentID
ProductID | ProductName | ProductLotID | ExpirationDate | LotNumber | LocationInventoryAdjustmentID | Created | count
--------: | :---------- | -----------: | :------------- | :-------- | ----------------------------: | :--------- | ----:
1 | Banana | 23 | 2024-01-01 | Chikita24 | 53 | 2020-07-07 | 150
3 | Orange | 21 | 2023-12-01 | Florida-9 | 51 | 2020-05-01 | 200
4 | Shirt | 22 | null | HANES-001 | 52 | 2020-06-01 | 300
db<>fiddle here

Case, group by , join on multiple table

I have 3 tables transaction , store and date. A column in store table needs to be assigned values based on conditions and that new column needs to be used in group by.
ASK is to find ` total sales across different banners for a particular time period.
i am using following query..
"""select sum(net_spend) as sales , d.fis_week_id, st.banner
from ( select s*,
CASE WHEN st.format IN ('S','S MINI','S HYPER') THEN 'S'
WHEN st.format = 'CHECKERS' THEN 'CHECKERS'
ELSE st.format END AS banner
from store_dim s) st
from transaction_item_fct tr
inner join date_dim d on d.date_id = tr.date_id
inner join store_dim_c s on st.store_id = tr.store_id
where d.fis_week_id >=201809 and d.fis_week_id<=201813
and tr.store_id = st.store_id
group by st.banner, d.fis_week_id
"""
Where I am getting wrong?
Below is the fabricated tables' data
Transaction table-
store_id week_id net_spend
1 12 345
1 11 788
2 13 556
3 11 300
Store table
store_id format
1 S
2 S MINI
3 S Hyper
4 Checker
Date table
week_id fis_week_id
11 201712
12 201717
Result expected is
week_id banner spend
11 S 888
11 Hyper 666
It's not 100% clear from your question but I think this query will do what you want. You will need to change the WHERE condition on d.fis_week_id as appropriate for your needs, I have made it appropriate for the demo case I set up.
SELECT d.week_id, st.banner, SUM(t.net_spend) AS sales, d.fis_week_id
FROM date_dim d
LEFT JOIN transaction_item t ON t.week_id = d.week_id
JOIN (SELECT store_id,
CASE WHEN format IN ('S', 'S MINI', 'S Hyper') THEN 'S'
WHEN format = 'Checker' THEN 'Checker'
ELSE format
END AS banner
FROM store_dim s) st
ON st.store_id = t.store_id
WHERE d.fis_week_id BETWEEN 201712 AND 201720
GROUP BY d.week_id, st.banner
I've expanded your demo data out a bit and created a test case at SQLFiddle:
CREATE TABLE transaction_item
(`store_id` int, `week_id` int, `net_spend` int);
INSERT INTO transaction_item
(`store_id`, `week_id`, `net_spend`)
VALUES (1, 12, 345), (1, 11, 788), (2, 13, 556), (3, 11, 300),
(4, 11, 440), (4, 12, 123), (5, 11, 100), (6, 13, 444);
CREATE TABLE store_dim
(`store_id` int, `format` varchar(7));
INSERT INTO store_dim
(`store_id`, `format`)
VALUES (1, 'S'), (2, 'S MINI'), (3, 'S Hyper'), (4, 'Checker'), (5, 'Checker'), (6, 'Other');
CREATE TABLE date_dim
(`week_id` int, `fis_week_id` int);
INSERT INTO date_dim
(`week_id`, `fis_week_id`)
VALUES (11, 201712), (12, 201717), (13, 201720);
Output:
week_id banner sales fis_week_id
11 Checker 540 201712
11 S 1088 201712
12 Checker 123 201717
12 S 345 201717
13 Other 444 201720
13 S 556 201720

Join data from rows to 1 row depending on the number of rows

I have the following tables:
user
userId name
1 Sam
2 Harold
3 John
othertable
id id2 number
1 111 12
1 222 23
1 333 33
2 111 12
2 444 11
3 555 12
3 222 44
The user table's userId matches the othertable's id column. Ideally I'd like to join the content of othertable to user depending on how many rows are present for that id. That's the output I'm aiming for:
e.g.
user
userId name 111 222 333 444 555
1 Sam 12 12 33
2 Harold 12 11
3 John 44 12
Any idea?
Update: The id2's values are limited. Only valid values 111, 222, 333, 444 and 555.
you may try out this 1... not sure if it meet you requirement...
CREATE TABLE users
(
userId int,
name varchar(max)
)
INSERT INTO USERS VALUES
(1, 'Sam'),
(2, 'Harold'),
(3, 'John')
CREATE TABLE othertable
(
id int,
id2 int,
number int
)
INSERT INTO othertable VALUES
(1, 111, 12),
(1, 222, 23),
(1, 333, 33),
(2, 111, 12),
(2, 444, 11),
(3, 555, 12),
(3, 222, 44)
SELECT
u.userId,
u.name,
SUM(CASE WHEN (id2=111) THEN number ELSE 0 END) AS [111],
SUM(CASE WHEN (id2=222) THEN number ELSE 0 END) AS [222],
SUM(CASE WHEN (id2=333) THEN number ELSE 0 END) AS [333],
SUM(CASE WHEN (id2=444) THEN number ELSE 0 END) AS [444],
SUM(CASE WHEN (id2=555) THEN number ELSE 0 END) AS [555]
FROM othertable o
INNER JOIN users u ON o.id = u.userId
GROUP BY u.userId, u.name
Please have a try. :)
UPDATE: Sorry, I'm not really familiar with MySQL. But I did tried my best
by changing the query into subquery and hope this can help you. If
this doesn't meet you requirement, I hope some other people can help
you.
UPDATE 2: Avoid using PIVOT

Trying to group by a field with latest date in SQL where another field is equal

Hey I have run into a problem - I thought it would be easy with just group by but it was a little more when time is a factor in the game.
I'm trying to get all the records on different mainQuestion_id which can have duplicates with different timestamp (and id ofc). This should be filtered so I get those which are equal to an activationCode_id and not with a subquestion which is the field subQuestion_id
So lets say I have this table
CREATE TABLE surveys_answer
(`id` int, `activationCode_id` int, `mainQuestion_id` int, `subQuestion_id` int, `timestamp` int);
INSERT INTO surveys_answer
(`id`, `activationCode_id`, `mainQuestion_id`, `subQuestion_id`, `timestamp`)
VALUES
(1, 1, 4, 0, 201313),
(2, 1, 4, 0, 201314),
(3, 2, 3, 1, 201315),
(4, 2, 4, 0, 201316),
(5, 1, 9, 1, 201317),
(6, 1, 6, 0, 201318),
(7, 1, 4, 1, 201319);
and I want the results which are latest by time or id which have activationCode_id = 1 and where subQuestion_id = 0
what I want is
2, 1, 4, 0, 201314
6, 1, 6, 0, 201318
I have been trying with group by, that misses (2, 1, 4, 0, 201314) and takes the first one (1, 1, 4, 0, 201313) which is not what I want
I thought this would work:
SELECT * FROM surveys_answer
WHERE activationCode_id = 1
AND subQuestion_id = 0
group by mainQuestion_id
I have been trying to get this to work:
SELECT a.*
FROM surveys_answer a
INNER JOIN
(
SELECT max(id) xx, mainQuestion_id
FROM surveys_answer
GROUP BY mainQuestion_id
) b ON a.mainQuestion_id = b.mainQuestion_id AND
a.id = b.xx AND a.activationCode_id = '1' AND a.subQuestion_id = 0
But this gives me zero results
http://www.sqlfiddle.com/#!2/3e1cd
Can some one help me out with this?
SELECT sa.*
FROM surveys_answer sa
WHERE timestamp = (
SELECT MAX(sa2.timestamp)
FROM surveys_answer sa2
WHERE sa2.activationCode_id = 1 AND sa2.subQuestion_id = 0 AND
sa2.mainQuestion_id = sa.mainQuestion_id
);
SQL Fiddle
Result based on your sample data:
| ID | ACTIVATIONCODE_ID | MAINQUESTION_ID | SUBQUESTION_ID | TIMESTAMP |
|----|-------------------|-----------------|----------------|-----------|
| 2 | 1 | 4 | 0 | 201314 |
| 6 | 1 | 6 | 0 | 201318 |
may be you can try this
SELECT surveys_answer .* , max(timestamp) as mx_time FROM surveys_answer
WHERE activationCode_id = 1
AND subQuestion_id = 0
group by timestamp