Sum of values of a field based on another field - mysql

I have a table as follows. I need to build a query to find the following.
(Sum of feeComponentValue where feeComponentCalc = 1) - (Sum of feeComponentValue where feeComponentCalc = 1) and it should be group by feeSettingId
Please help
CREATE TABLE `feevalues` (
`feevaluesId` int(11) NOT NULL,
`feeSettingId` int(11) NOT NULL,
`feeComponentId` int(11) NOT NULL,
`feeComponentValue` int(11) DEFAULT NULL,
`feeComponentCalc` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table feevalues
INSERT INTO `feevalues`
(`feevaluesId`, `feeSettingId`, `feeComponentId`, `feeComponentValue`, `feeComponentCalc`)
VALUES
(27, 29, 1, 1000, 1),
(28, 29, 12, 2000, 1),
(29, 29, 3, 3000, 1),
(30, 29, 4, 4000, 1),
(103, 30, 3, 1000, 1),
(104, 30, 1, 2000, 1),
(105, 30, 3, 3000, 1),
(106, 30, 1, 4000, 1),
(107, 30, 14, 5000, 2),
(108, 30, 2, 6000, 2),
(109, 30, 13, 7000, 2),
(110, 30, 2, 8000, 2),
(111, 31, 1, 1000, 1),
(112, 31, 3, 2000, 1),
(113, 31, 13, 4000, 2),
(114, 31, 2, 3000, 2),
(122, 32, 1, 1, 1),
(123, 32, 3, 3, 1),
(124, 32, 4, 4, 1),
(125, 32, 2, 5, 2),
(126, 32, 14, 6, 2),
(127, 32, 13, 7, 2);
--

I assume you mean ...where feeComponentCalc = 2... in the 2nd case, right?
With conditional aggregation:
select
feeSettingId,
sum(case feeComponentCalc when 1 then feeComponentValue else 0 end) -
sum(case feeComponentCalc when 2 then feeComponentValue else 0 end) as result
from feevalues
group by feeSettingId
See the demo.
Or:
select
feeSettingId,
sum(
case feeComponentCalc
when 1 then feeComponentValue
when 2 then -feeComponentValue
else 0
end
) as result
from feevalues
group by feeSettingId
See the demo.
Results:
| feeSettingId | result |
| ------------ | ------ |
| 29 | 10000 |
| 30 | -16000 |
| 31 | -4000 |
| 32 | -10 |

Related

OR operator applying to entire WHERE clause

SELECT DISTINCT
customers.customer_id,
services.name
FROM users
INNER JOIN customers ON users.user_id=customers.user_id
LEFT JOIN appointments ON customers.customer_id=appointments.customer_id
INNER JOIN pets ON customers.customer_id=pets.customer_id
INNER JOIN services on appointments.service_id=services.service_id
WHERE ((appointments.customer_id IS NULL)
OR NOT (appointments.date > (SELECT SUBDATE(CURDATE(), 365)))
OR ((appointments.date > (SELECT SUBDATE(CURDATE(), 365)))
AND services.name NOT LIKE '%General Health Checkup%'))
GROUP BY customers.customer_id
I am trying to find all customers who are due a yearly general health checkup
this requires them to have:
a) never have appointments
b) not had an appointment in the past year
c) had an appointment in the past year but it wasn't a general health checkup
I assumed the final OR im my WHERE clause would only apply to that that operation i.e.
OR ((appointments.date > (SELECT SUBDATE(CURDATE(), 365))) AND services.name NOT LIKE '%General Health Checkup%'))
however it is ruling out all options for services named General Health Checkup.
How can i only apply this to that area of the where clause so that general health check up appointments can get through but only if they are from over a year ago.
Thanks !
appointments table:
CREATE TABLE `appointments` (
`appointment_id` int(8) NOT NULL,
`customer_id` int(8) DEFAULT NULL,
`service_id` int(4) DEFAULT NULL,
`staff_id` int(6) DEFAULT NULL,
`pet_id` int(9) DEFAULT NULL,
`date` date NOT NULL,
`start_time` time NOT NULL,
`status` enum('Open','Cancelled','Completed','') NOT NULL,
`create_date` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `appointments`
--
INSERT INTO `appointments` (`appointment_id`, `customer_id`, `service_id`, `staff_id`, `pet_id`, `date`, `start_time`, `status`, `create_date`) VALUES
(1, 1, 2, 1, 1, '2017-03-22', '10:00:00', 'Completed', '2022-03-16 11:28:46'),
(2, 3, 2, 1, 6, '2021-06-18', '12:00:00', 'Completed', '2021-06-15 11:01:43'),
(3, 2, 2, 1, 2, '2020-07-17', '13:00:00', 'Completed', '2020-05-14 11:30:18'),
(4, 3, 2, 1, 5, '2020-07-10', '14:00:00', 'Completed', '2020-05-21 11:30:18'),
(5, 4, 3, 1, 7, '2020-09-17', '10:00:00', 'Completed', '2022-03-16 12:31:59'),
(6, 8, 2, 1, 11, '2022-03-17', '12:00:00', 'Cancelled', '2022-03-17 23:44:56'),
(7, 4, 2, 7, 7, '2022-03-17', '10:00:00', 'Cancelled', '2022-03-17 23:50:11'),
(8, 1, 1, 13, 1, '2022-03-17', '13:00:00', 'Completed', '2022-03-18 00:28:10'),
(9, 7, 2, 13, 9, '2022-03-18', '15:00:00', 'Cancelled', '2022-03-18 13:16:37'),
(10, 7, 1, 13, 10, '2022-03-18', '16:00:00', 'Cancelled', '2022-03-18 13:48:12'),
(11, 1, 1, 13, 1, '2022-03-22', '11:00:00', 'Completed', '2022-03-22 12:34:55'),
(12, 11, 1, 13, 11, '2022-03-23', '13:00:00', 'Completed', '2022-03-23 15:28:22'),
(13, 9, 3, 13, 12, '2022-03-26', '13:00:00', 'Completed', '2022-03-26 13:13:46'),
(14, 35, 2, 13, 16, '2022-03-27', '10:00:00', 'Completed', '2022-03-27 16:09:14'),
(15, 34, 2, 13, 20, '2022-03-28', '10:00:00', 'Completed', '2022-03-28 10:05:41'),
(16, 33, 1, 13, 20, '2022-03-28', '12:00:00', 'Completed', '2022-03-28 11:40:50'),
(17, 8, 2, 13, 20, '2022-03-16', '14:00:00', 'Completed', '2022-03-28 12:31:42'),
(18, 15, 2, 13, 20, '2022-03-28', '14:00:00', 'Completed', '2022-03-28 12:33:47'),
(19, 31, 4, 13, 20, '2022-03-29', '00:00:00', 'Completed', '2022-03-29 14:20:04'),
(20, 31, 4, 13, 20, '2022-03-29', '10:00:00', 'Completed', '2022-03-29 14:20:42'),
(21, 1, 1, 13, 1, '2022-03-30', '11:00:00', 'Completed', '2022-03-30 15:18:23'),
(22, 33, 4, 13, 22, '2022-03-30', '12:00:00', 'Completed', '2022-03-30 15:22:02'),
(23, 3, 1, 13, 5, '2022-03-30', '13:00:00', 'Open', '2022-03-30 15:22:02'),
(24, 4, 1, 13, 7, '2022-03-30', '13:30:00', 'Completed', '2022-03-30 15:24:52'),
(25, 7, 2, 13, 10, '2022-03-30', '14:30:00', 'Open', '2022-03-30 15:26:11'),
(26, 12, 1, 7, 8, '2022-04-21', '10:00:00', 'Open', '2022-04-21 12:54:10'),
(27, 2, 1, 1, 8, '2022-04-21', '10:00:00', 'Cancelled', '2022-04-21 13:16:23'),
(28, 17, 4, 1, 20, '2022-04-21', '10:00:00', 'Completed', '2022-04-21 13:18:41'),
(29, NULL, 6, 7, 21, '2022-04-21', '12:00:00', 'Completed', '2022-04-21 16:22:44'),
(30, 13, 2, 3, 5, '2022-04-21', '14:00:00', 'Open', '2022-04-21 17:42:10'),
(31, 5, 1, 9, 14, '2022-04-22', '11:00:00', 'Open', '2022-04-22 16:16:57');
services table:
CREATE TABLE `services` (
`service_id` int(4) NOT NULL,
`name` varchar(100) NOT NULL,
`description` text NOT NULL,
`average_time` int(3) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `services`
--
INSERT INTO `services` (`service_id`, `name`, `description`, `average_time`) VALUES
(1, 'Consultation', 'General consultation to help you find the best path to good health for your pet.', 30),
(2, 'General Health Checkup', 'Review of your pets health.', 30),
(3, 'Microchip Insertion', 'Keep your dog safe and trackable with microchip.', 90),
(4, 'Puppy Vaccination', 'Initial puppy vaccination', 30),
(6, 'Booster Vaccination', 'Regular booster vaccincation service.', 30);
I would just check if the customer had a service_id=2 in the last year:
SELECT
customers.customer_id,
GROUP_CONCAT(CONCAT(services.name,'(',appointments.date,')')) as services
FROM users
INNER JOIN customers ON users.user_id=customers.user_id
LEFT JOIN appointments ON customers.customer_id=appointments.customer_id
INNER JOIN pets ON customers.customer_id=pets.customer_id
INNER JOIN services on appointments.service_id=services.service_id
WHERE SUBDATE(CURDATE(), 365) > (SELECT MAX(date)
FROM appointments
WHERE customers.customer_id=appointments.customer_id
AND appointments.service_id=2)
GROUP BY customers.customer_id
output:
customer_id
services
1
General Health Checkup(2017-03-22),Consultation(2022-03-30),Consultation(2022-03-22),Consultation(2022-03-17)
2
General Health Checkup(2020-07-17),Consultation(2022-04-21)
see: DBFIDDLE

MYSQL - Count consecutive identical values

I have a MySQL table which basically stores a list of points with reference of the player id who made that point.
My setup is:
CREATE TABLE `points` (
`id` int(10) unsigned NOT NULL,
`player_id` int(10) unsigned NOT NULL,
`game_id` int(10) unsigned NOT NULL,
`score` int(11) NOT NULL,
`isScore` tinyint(1) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
);
INSERT INTO `points` (`id`, `player_id`, `game_id`,
`score`, `isScore`, `created_at`, `updated_at`)
VALUES
(16, 11, 60, 4, 0, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(17, 11, 60, 5, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(18, 12, 60, 6, 0, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(19, 12, 60, 7, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(20, 12, 60, 8, 0, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(21, 12, 60, 9, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(22, 8, 60, 10, 0, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(23, 8, 60, 11, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(24, 12, 60, 12, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(25, 12, 60, 13, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(26, 5, 60, 14, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(27, 12, 60, 15, 0, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(28, 12, 60, 16, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(29, 10, 60, 17, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39'),
(30, 10, 60, 18, 1, '2016-12-28 12:19:39', '2016-12-28 12:19:39');
Here's a SQLfiddle with this setup: http://sqlfiddle.com/#!9/f6ca9f8
I want to get the number of maximum consecutive point made by a player for each player.
So in the sqlfiddle the result will be something like:
+-----------+------------------------+
| player_id | max_consegutive_points |
+-----------+------------------------+
| 12 | 4 |
| 8 | 2 |
| 5 | 1 |
| 10 | 2 |
+-----------+------------------------+
Your question is missing some key points (no pun intended). For example: what is considered a consecutive identical value? You need to define if those values will be listed ordered by ID, created_at, ID+game_id, etc.
So let's say the order is given by the ID, because it may be autoincremental and it's only natural that points are inserted in the order they happened.
However, the answer will be to create a query with a counter variable (accum) that increases if the player is the same than the one in the former row, or resets to 1 if the player changes:
select
points.id,
points.player_id,
points.game_id,
points.created_at,
#counter:=IF(#playerid=points.player_id,#counter+1,1) as accum,
#playerid:=points.player_id as current_player_id
from points,
(select #counter:=1) c,
(select #playerid:=0) p
order by points.id
See SQLFiddle here
Having the result of that table, and putting it into a view or temporary table that we may call "accums", you just need to do
SELECT player_id, max(accum)
FROM accums
GROUP BY player_id
select distinct(player_id), MAX(score) from points group by player_id;

MySQL query to limit maximums and minimums

I have the following table:
INSERT INTO `test_table` (`id`, `x`, `y`) VALUES (1, 0, 10), (2, 6, 10),
(3, 9, 10), (4, 2, 9), (5, 4, 9), (6, 3, 8), (7, 7, 8), (8, 9, 8),
(9, 0, 7), (10, 2, 7), (11, 5, 7), (12, 10, 7), (13, 2, 6), (14, 7, 6),
(15, 0, 5), (16, 4, 5), (17, 9, 5), (18, 1, 4), (19, 3, 4), (20, 5, 4),
(21, 10, 4), (22, 8, 3), (23, 3, 2), (24, 6, 2), (25, 2, 1), (26, 8, 1),
(27, 0, 0), (28, 5, 0), (29, 6, 0), (30, 10, 0);
and it represent the following picture:
The numbers in the red or pink cells represent the "id" and the coordinates for those cells are "x" and "y".
All I have to do is to come up with a query to show all the cells (only the red ones), excluding the 10 cell within the gray square.
So far I have this:
SELECT * FROM `test_table` WHERE
x between 0 and 10 and
x not between 2 and 8 and
y between 0 and 10 and
y not between 2 and 7
order by id ASC
LIMIT 30
Imagining that the grid goes bellow 0 and beyond 10 in both directions (not showing in the picture); the query must have way too many limits. Anyways, the output is not what I want because it only give me the cells in the corners (the ones within the green areas): 1, 3, 8, 27 and 30
another approach will be to subtract this query:
SELECT * FROM `test_table` WHERE
x between 2 and 8 and
y between 2 and 7
order by id ASC
LIMIT 30
from this one:
SELECT * FROM `test_table` WHERE
x between 0 and 10 and
y between 0 and 10
order by id ASC
LIMIT 30
...but once again; i am unable to do it :(
It just needs to exclude x[2:8],y[2,7].
Try this:
SELECT * FROM `test_table`
WHERE NOT(x between 2 and 8 AND y between 2 and 7);
Just to add the other "side" limits; the final query was implemented like this:
SELECT * FROM
`test_table` WHERE
x between 0 and 10 and
y between 0 and 10 and
not
(x between 2 and 8 and
y between 2 and 7)

SELECT query for TableA and TableB IF TableB.col value (0 OR 1) I create new columns for 0 and 1

I have a Table pictures
create table pictures (
picture_id int(10) unsigned NOT NULL AUTO_INCREMENT,
mall_id float NOT NULL,
shop_id float NOT NULL,
picture_islogo int(11) NOT NULL ,
picture_path varchar(255) NOT NULL,
PRIMARY KEY (picture_id)
) ;
Sample Data for pictures
INSERT INTO pictures VALUES
(1, -1, 1, 1, 'photo.jpg'),
(2, -1, 2, 1, 'photo.jpg'),
(3, -1, 3, 1, 'photo.jpg'),
(4, -1, 4, 1, 'photo.jpg'),
(5, -1, 5, 1, 'photo.jpg'),
(6, -1, 6, 1, 'photo.jpg'),
(7, -1, 7, 1, 'photo.jpg'),
(8, -1, 8, 1, 'photo.jpg'),
(9, -1, 9, 1, 'photo.jpg'),
(10, -1, 10, 1, 'photo.jpg'),
(11, -1, 11, 1, 'photo.jpg'),
(12, -1, 12, 1, 'photo.jpg'),
(13, -1, 13, 1, 'photo.jpg'),
(14, -1, 13, 0, 'photo.jpg');
and other table malls
create table malls (
mall_id float NOT NULL AUTO_INCREMENT,
user_id float NOT NULL,
mall_displaysname varchar(255) NOT NULL,
mall_description text NOT NULL,
mall_contact varchar(14) NOT NULL,
mall_logo_picture_id int(11) NOT NULL,
mall_background_picture_id int(11) NOT NULL,
PRIMARY KEY (mall_id)
);
Sample Data for malls
INSERT INTO malls VALUES
(1, 2, 'mall', 'description', '+60 12 3456789', 14, 36),
(2, 5, 'mall 2', 'description', '+60 12 3456789', 15, 37),
(3, 6, 'mall 3', 'description ', '+60 12 3456789', 16, 38),
(4, 13, 'Multimedia University', 'description ', '+60 12 3456789', 17, 39),
(5, 18, 'Setia Walk', 'description ', '+60 12 3456789', 18, 40),
(6, 20, 'Ampang Point', 'description ', '+60 12 3456789', 19, 41),
(7, 21, 'Alamanda Plaza', 'description ', '+60 12 3456789', 20, 42),
(8, 22, 'Subang Parade', 'description ', '+60 12 3456789', 21, 43),
(9, 26, 'مجمع العرب - جدة', 'description ', '+60 12 3456789', 22, 44);
Where mall_background_picture_id and mall_background_picture_id reference to pictures.picture_id and Depend on the value of pictures.picture_islogo (0: Logo, 1:background or others)
i want a query to that returns the pictures.picture_path depends on the value of pictures.picture_islogo (0: Logo, 1:background or others)
the data that I need to get
mall_id , mall_displaysname, mall_logo, mall_background , mall_description, and mall_contact
Where mall_logo is the photo_path of the condition
pictures.picture_islogo = 1
and same thing with **mall_background** but
picture_islogo = 0
AND
mall_logo_picture_id = picture_id
Put the extra condition into the join condition:
select * -- you can chose what columns you want
from malls m
left join pictures pl on m.mall_logo_picture_id = pl.picture_id
and pl.picture_isLogo = 1
left join pictures pb on m.mall_background_picture_id = pb.picture_id
and pb.picture_isLogo = 0
To have left joins, the extra conditions must be in the join condition, which is evaluated as the join is being made.
If you put them in the where clause, which is evaluated after the join is made, you force the outer join to be an inner join, because the join must be successful fur the extra condition to be true.

Build query 'average amount by month' with filling gaps from previous month

I have a problem. I need to build report query to calculate average amount by month.
If user from account does not buy anything in this month I should use amount from previous month.
This is my data: As you can see I have one account operation on July (2014-07) and one operation in August (2014-08).
CREATE TABLE IF NOT EXISTS `value_hist` (
`id` int(11) NOT NULL,
`account_id` int(11) DEFAULT NULL,
`data_channel_id` int(11) DEFAULT NULL,
`status` tinyint(1) NOT NULL,
`amount` decimal(19,4) NOT NULL COMMENT '(DC2Type:money)(DC2Type:money)',
`created_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;
INSERT INTO `value_hist` (`id`, `account_id`, `data_channel_id`, `status`, `amount`, `created_at`)
VALUES
(28, 19, 2, 0, 100.0000, '2014-08-22 14:59:47'),
(29, 19, 2, 0, 11.0000, '2014-09-22 15:05:36'),
(30, 19, 2, 0, 22.0000, '2014-09-22 15:10:59'),
(31, 19, 2, 0, 0.0000, '2014-09-22 15:14:19'),
(32, 19, 2, 0, 22.0000, '2014-09-22 15:15:58'),
(33, 19, 2, 0, 0.0000, '2014-09-22 15:17:29'),
(34, 1, 2, 0, 50.0000, '2014-07-22 15:20:10'),
(35, 1, 2, 0, 0.0000, '2014-09-22 15:23:37'),
(36, 1, 2, 0, 100.0000, '2014-09-22 15:23:39'),
(37, 1, 2, 1, 0.0000, '2014-09-22 15:30:53'),
(38, 19, 2, 0, 100.0000, '2014-09-22 15:30:53'),
(39, 28, 2, 1, 0.0000, '2014-09-22 15:36:44'),
(40, 19, 2, 1, 0.0000, '2014-09-22 15:45:24'),
(41, 6, 2, 0, 0.0000, '2014-09-22 15:48:54'),
(42, 6, 2, 0, 200.0000, '2014-09-22 15:49:34'),
(43, 6, 2, 1, 33.0000, '2014-09-22 15:50:18');
I have built a query. Which can get average value from each month
SELECT
data_channel_id,
test2.monthaa,
AVG(h1.amount) as amount
FROM
value_hist h1
JOIN (
SELECT MAX(dd.`id`) as id, EXTRACT(month from dd.created_at) as monthaa
FROM value_hist dd
where dd.created_at > '01.01.2014'
GROUP BY data_channel_id, dd.account_id, monthaa
) test2 ON test2.id = h1.id
WHERE
created_at > '01.01.2014'
GROUP BY data_channel_id, monthaa
The result return average value from each month
| channel | month | amount |
| 2 | 7 | 50.0000|
| 2 | 8 | 100.000|
| 2 | 9 | 8.25000|
But I should get this result, I should consider previous month amount. For example (100 + 50) / 2, because account 1 did not buy in August (2014-08)
| channel | month | amount |
| 2 | 7 | 50.0000|
| 2 | 8 | 75.0000|
| 2 | 9 | 8.25000|
Update: There was only one account in July(7) thus amout equal to 50.00; (50.00 / 1 = 50.00).
In August(8) second account has come therefore amount equal to 75.00; ((100.00 + 50) / 2 = 75.00) because I have two accounts in August(8).