Complex insert query with insert into and where subquery in Laravel - mysql

I am trying to figure out the best way to code this using Query Builder or Eloquent.
In it's simplest form it should prevent insert of a new buy request if an item already in transactions table. I haven't found any useful reference other than running a complete raw query.
INSERT INTO `transactions`
(`user_id`, `item_id`, `type`, `created_at`, `updated_at`)
SELECT
1, 186808, 'bought', NOW(), NOW()
WHERE
(
SELECT
SUM(
CASE
WHEN `type` = 'bought' THEN 1
WHEN `type` = 'sold' THEN -1
END
)
FROM `transactions`
WHERE
(`item_id`, `user_id`) = (186808, 1)
GROUP BY `item_id`
) = 0

In Laravel, you can use DB::raw(<your_complex_query_here>) to achieve this.
DB::raw("INSERT INTO `transactions`
(`user_id`, `item_id`, `type`, `created_at`, `updated_at`)
SELECT
1, 186808, 'bought', NOW(), NOW()
WHERE
(
SELECT
SUM(
CASE
WHEN `type` = 'bought' THEN 1
WHEN `type` = 'sold' THEN -1
END
)
FROM `transactions`
WHERE
(`item_id`, `user_id`) = (186808, 1)
GROUP BY `item_id`
) = 0");
For more, refer to Laravel docs here https://laravel.com/docs/5.7/queries#raw-expressions

Related

Mysql query to get created and resolved defect group by month

The table I am using is like bellow:
CREATE TABLE IF NOT EXISTS `tickets` (
`id` int(6) unsigned NOT NULL,
`created` timestamp ,
`resolved` timestamp ,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tickets` (`id`, `created`, `resolved`) VALUES
('1', '2021-01-01', '2021-01-12'),
('2', '2021-02-25', '2021-01-15'),
('3', '2021-03-10', '2021-03-22'),
('4', '2021-03-10', '2021-03-22'),
('5', '2021-03-10', '2021-03-22'),
('6', '2021-03-11', '2021-03-22'),
('7', '2021-03-13', '2021-03-22'),
('8', '2021-03-13', '2021-03-22'),
('9', '2021-04-01', '2021-03-12');
Now I want a query to show me the table with columns like
Month, NumberOfticketsCreated, NumberOfTicketsResolved.
Here is what I tried so far but it does not return what I expect:
SELECT
YEAR(`created`) AS y
, MONTH(`created`) AS m
, COUNT(`created`) as NumberOfticketsCreated
, count(`resolved`) as NumberOfTicketsResolved
FROM tickets
GROUP BY y, m;
Create table t1 with group by 'created'
Create table t2 with group by 'resolved'
then left join above two tables.
select t1.y, t1.m, t1.NumberOfticketsCreated, t2.NumberOfTicketsResolved
from
(SELECT
YEAR(`created`) AS y
, MONTH(`created`) AS m
, MONTH(`resolved`) AS n
, COUNT(`created`) as NumberOfticketsCreated
, count(`resolved`) as NumberOfTicketsResolved
FROM tickets
GROUP BY m) as t1
left join
(SELECT
YEAR(`created`) AS y
, MONTH(`created`) AS m
, MONTH(`resolved`) AS n
, COUNT(`created`) as NumberOdticketsCreated
, count(`resolved`) as NumberOfTicketsResolved
FROM tickets
GROUP BY n) as t2
on t1.m = t2.m

Maria DB - update row with value of previous row + constant

I have table called dobridol with several column.
CREATE TABLE IF NOT EXISTS `dobridol` (
`id` int(6) unsigned NOT NULL,
`dt` varchar(200) NOT NULL,
`p2` int(6) NOT NULL,
`p6` int(6) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `dobridol` (`id`, `dt`, `p2`,`p6`) VALUES
('1', '2021-02-28 23:50:00', '100', '600'),
('2', '2021-02-28 23:55:00', '200', '700'),
('3', '2021-03-01 00:00:00', '300', '800'),
('4', '2021-03-01 00:05:00', '400', '900'),
('5', '2021-03-01 00:10:00', '400', '900'),
('6', '2021-03-01 00:15:00', '400', '900'),
('7', '2021-03-01 00:20:00', '500', '1000'),
('8', '2021-03-01 00:25:00', '600', '1100');
The table has values for January and March also.
I want to be able to UPDATE table like that:
I select period as month, then I add constant value (in this case I added 39) to p6 ONLY WHEN p2 value is different than previous row p2.
If this is the case I have to add 39 to PREVIOUS row p6 value.
update dobridol join
(select tt.*,
sum(case when p2 <> prev_p2 then 1 else 0 end) over (order by dt) as cnt
from (select tt.*,
lag(p2) over (order by dt) as prev_p2
from dobridol tt
) tt
) tt
on tt.id = dobridol.id
set dobridol.p6 = cnt * 39 + <PREVIOUS_ROW_p6_VALUE_HAS_TO_BE_HERE>
where cnt > 0
The query should look-like this but I have to replace this <PREVIOUS_ROW_VALUE_HAS_TO_BE_HERE> with the right syntax of picking last row p6. How can I pick it?
Also where to add clause
dobridol.dt BETWEEN '2021-03-01' AND '2021-03-30'
in the SQL query?
If I understand correctly, you can use lag():
update dobridol join
(select tt.*,
lag(p6) over (order by dt) as prev_p6,
sum(case when p2 <> prev_p2 then 1 else 0 end) over (order by dt) as cnt
from (select tt.*,
lag(p2) over (order by dt) as prev_p2
from dobridol tt
) tt
) tt
on tt.id = dobridol.id
set dobridol.p6 = tt.cnt * 39 + tt.prev_p6
where cnt > 0 ;
Here is a db<>fiddle.

Mysql select from one table

My Table structure is
id;product_id;sell_type;sell_state
sell_type: BUY, SELL
sell_state: OPEN, FILLED, CANCELED
How to select only product_id with each 2 operation, BUY & SELL in sell_type and FILLED in sell_state
CREATE TABLE `orderlist` (
`id` int(10) UNSIGNED NOT NULL,
`product_id` int(11) NOT NULL,
`sell_type` enum('BUY','SELL') DEFAULT NULL,
`sell_state` enum('OPEN','FILLED','CANCELED') DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `orderlist` (`id`, `product_id`, `sell_type`, `sell_state`) VALUES
(7, 1, 'BUY', 'FILLED'),
(8, 1, 'SELL', 'FILLED'),
(9, 2, 'BUY', 'FILLED'),
(10, 3, 'SELL', 'FILLED');
You could use a group by and count(distinct sell_state) = 2
select product_id
from orderlist
where sell_state ='FILLED'
and sell_type in ('BUY', 'SELL')
group by product_id
having count(distinct sell_type) = 2
http://sqlfiddle.com/#!9/c41301/2
try this select query
SELECT t1.*
FROM temp t1
INNER JOIN temp t2
ON t1.product_id = t2.product_id AND FIND_IN_SET('FILLED',t1.sell_state) > 0
WHERE FIND_IN_SET('BUY',t1.sell_type) > 0 AND
CONCAT(",", t2.sell_type, ",") REGEXP ",(BUY|SELL),"
GROUP BY t1.product_id
demo : http://sqlfiddle.com/#!9/7fcd9/64
try this
SELECT product_id
FROM orderlist
WHERE sell_state = 'FILLED' AND sell_type IN ('BUY','SELL')
GROUP BY product_id
HAVING COUNT(product_id) > 2
demo: http://sqlfiddle.com/#!9/464959/2

MySQL - UPDATE or INSERT if exists

I have a table in a MySQL database that needs to be updated if an instance does not already exist. I have tried the following methods but are no closer to a solution:
SET #last_message_id = (
SELECT id FROM mailbox_last_message WHERE conversationId = '13'
);
BEGIN TRANSACTION;
UPDATE mailbox_last_message SET conversationId = '13', initiatorMessageId = '20', interlocutorMessageId = '10'
WHERE id = #last_message_id
IF (##ROWCOUNT = 0)
INSERT INTO mailbox_last_message ( id , conversationId , initiatorMessageId , interlocutorMessageId )
VALUES ('' , '13', '20', '0' );
COMMIT;
And this one:
SET #last_message_id = (
SELECT `id` FROM mailbox_last_message WHERE `conversationId` = 13);
INSERT INTO mailbox_last_message ( id , conversationId , initiatorMessageId , interlocutorMessageId )
VALUES ( '', '13', '20', '0' ) ON DUPLICATE
KEY UPDATE id = #last_message_id
This solution creates a new instance with conversationId 13 which is not what I want. I've tried to search the documentation and various tutorials/examples online but I can't find any suitable solution. Any help will be greatly appreciated.
If you have a unique index on the fields that you want to keep unique, you can use INSERT IGNORE INTO syntax
SET #last_message_id = (
SELECT id FROM mailbox_last_message WHERE conversationId = '13'
);
INSERT IGNORE INTO mailbox_last_message ( id , conversationId , initiatorMessageId , interlocutorMessageId )
VALUES ('' , '13', '20', '0' ) ON DUPLICATE KEY UPDATE id = #last_message_id;

Using IF in MySQL's SELECT subquery

I have the following query:
SELECT `users`.`id`, `login`, `fullname`, `active`, `sex`, `height`,
`special-title`, `language`, `body-fat`,
`main-photo-id`, `main-photo-offset`, `weight`,
`objective`, `level`, `config-comments-type`,
(
SELECT `type`
FROM `users-pro`
WHERE
(`user` = `users`.`id`)
AND
(`starts` <= $time)
AND(
(`ends` > $time)
OR
(`ends` IS NULL)
)
LIMIT 1
) as `account_type`
FROM `users`
I'm wondering how/where do I add an IF statement, so that if the inner SELECT returns NULL (no entries for the user in users-pro, a value 1 would be returned.
This is what I usually do if there is no subquery:
SELECT IF(`rank` = 1, `rank`, 0) as `rank`
However, I don't know how to do this with a subquery.
You can use the ANSI standard coalesce() function for this, by wrapping the subquery in it:
SELECT `users`.`id`, `login`, `fullname`, `active`, `sex`, `height`,
`special-title`, `language`, `body-fat`,
`main-photo-id`, `main-photo-offset`, `weight`,
`objective`, `level`, `config-comments-type`,
coalesce((
SELECT `type`
FROM `users-pro`
WHERE
(`user` = `users`.`id`)
AND
(`starts` <= $time)
AND(
(`ends` > $time)
OR
(`ends` IS NULL)
)
LIMIT 1
), 1) as `account_type`
FROM `users`