How to query and group every continuous number series in MySQL? - mysql

I have this freight.or_nos table which contains series of receipt numbers. I want to list all the or's being issued excluding the status='Cancelled' making the series broken in groups.
For example I have this receipt stab 125001-125050, and 125020 is cancelled so the listing result would be:
+-------------------------------------------------------+
| OR Start | OR End | Quantity | Amount |
+-------------------------------------------------------+
| 125001 | 125019 | 19 | |
+-------------------------------------------------------+
| 125021 | 125050 | 30 | |
+-------------------------------------------------------+
This seems to be a tough query.

Thanks for reading but I already made it, just now! :)
Here's my query(disregard the other characters it's form our CGI):
{.while SELECT `start`,`end`,or_prefix,or_suffix,SUM(a.amount) AS g_total,COUNT(*) AS qcount FROM (SELECT l.id AS `start`,( SELECT MIN(a.id) AS id FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS a LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS b ON a.id = b.id - 1 WHERE b.id IS NULL AND a.id >= l.id ) AS `end` FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS l LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS r ON r.id = l.id - 1 WHERE r.id IS NULL) AS k LEFT JOIN freight.`or_nos` a ON a.`or_no` BETWEEN k.start AND k.end AND DATE(a.`or_date`)='#user_date`DATE' AND a.log_user =0#user_teller AND IF(a.status='Default' AND a.amount=0,0,1) AND a.status!='Cancelled' GROUP BY `start`}
{.start}{.x.24.12:end}{.x`p0.40.-5:qcount}{.x`p2.57.-15:g_total}{.asc 255}
{.wend}{.asc 255}

Related

SQL update multiple table using inner join

I have three tables "batch", "batchyield", "batchsop"
BATCH
|----------|--------------|----------------|-------|
| batch_id | batch_status | actual_produce | stage |
|----------|--------------|----------------|-------|
BATCHYIELD
|--------------|----------------|
| batch_id | actual_harvest |
|--------------|----------------|
BATCHSOP
|--------------|----------------|
| batch_id | current_status |
|--------------|----------------|
I am trying to update two tables at a time they all are connected with a foreign key
I have written a SQL query for that
UPDATE b SET
b.batch_status = 'completed', b.stage = 'flowering',
b.actual_produce = SUM(byl.actual_harvest),
bsop.current_status='3'
from igrow.farm_management_batch b
INNER JOIN igrow.farm_management_batchyield byl ON b.id = byl.batch_id
INNER JOIN igrow.sop_management_batchsopmanagement bsop ON b.id = bsop.batch_id
WHERE end_date < "2022-07-10 00:00:00.000000" and end_date is not null and (batch_status = "running" or batch_status = "to_start")
BUT It says the query is wrong
UPDATE igrow.farm_management_batch b
INNER JOIN ( SELECT batch_id, SUM(actual_harvest) actual_harvest
FROM igrow.farm_management_batchyield
GROUP BY batch_id ) byl ON b.id = byl.batch_id
INNER JOIN igrow.sop_management_batchsopmanagement bsop ON b.id = bsop.batch_id
SET b.batch_status = 'completed',
b.stage = 'flowering',
b.actual_produce = byl.actual_harvest,
bsop.current_status='3'
WHERE end_date < "2022-07-10 00:00:00.000000"
-- and end_date is not null
AND b.batch_status IN ("running", "to_start")
end_date is not null is excess (if previous is true then this is true too), commented.
PS. There is no end_date column in shown tables - where it is taken from?

Query to group without lost IF function

I created a query to search for all my stock products that are in orders placed, and I created an alias "total_vendido" that adds the products when they are kits or units, so far this is ok. But now I need to group the sizes and add this "total_vendido" alias by size.
Query:
SELECT `gp`.`id`, `gp`.`data`, `gp`.`status`, `gp`.`situacao`, `gp`.`nome`,
`gp`.`razao_social`, `gp`.`email`, `gp`.`telefone`,
`itens`.*,
IF(itens.tipo = 'K',
SUM(itens.qtde_prod) * itens.qtde_lote,
SUM(itens.qtde_prod)
) AS total_vendido,
`estoq`.`titulo`
FROM `ga845_pedidos_view` `gp`
JOIN `ga845_pedido_itens` `itens` ON `itens`.`pedido_id` = `gp`.`id`
JOIN `ga845_produtos` `prod` ON `prod`.`id` = `itens`.`produtos_id`
JOIN `ga845_produtos_estoque` `estoq` ON `estoq`.`id` = `prod`.`estoques_id`
WHERE `gp`.`situacao` IN('Pedido Realizado', 'Pagamento Aprovado',
'Pedido em Separação', 'Pedido Separado')
AND date(gp.data) >= '2020-07-25'
AND date(gp.data) <= '2020-07-25'
AND `estoq`.`id` IN('24')
GROUP BY `itens`.`tamanho_prod`, `estoq`.`id`
ORDER BY `estoq`.`id` ASC, `itens`.`tamanho_prod` ASC
Current result (only important columns)
tamanho_prod | tipo | total_vendido
G | K | 5
G | U | 1
M | K | 1
P | U | 8
Expected result (only important columns)
tamanho_prod | total_vendido
G | 6
M | 1
P | 8
Code related to Expected result (only important columns)
SELECT
, `itens`.`tamanho_prod`
, SUM( IF(itens.tipo = 'K',
itens.qtde_prod * itens.qtde_lote,
itens.qtde_prod
) AS total_vendido
FROM `ga845_pedidos_view` `gp`
JOIN `ga845_pedido_itens` `itens` ON `itens`.`pedido_id` = `gp`.`id`
JOIN `ga845_produtos` `prod` ON `prod`.`id` = `itens`.`produtos_id`
JOIN `ga845_produtos_estoque` `estoq` ON `estoq`.`id` = `prod`.`estoques_id`
WHERE `gp`.`situacao` IN('Pedido Realizado', 'Pagamento Aprovado',
'Pedido em Separação', 'Pedido Separado')
AND date(gp.data) >= '2020-07-25'
AND date(gp.data) <= '2020-07-25'
AND `estoq`.`id` IN('24')
GROUP BY `itens`.`tamanho_prod`
ORDER BY `itens`.`tamanho_prod` ASC
if you want an aggregated result just for itens.tamanho_prod .. then you should use group by only for this column ... and move the SUM() outside the if condition

Writing more better SQL

I've got a query here that's painfully slow. Part of the problem may be that tableA in the sub-query has a quite substantial size in comparison to the other tables.
TABLES STRUCTURE
*-------------------*------------------*-------------------*
| ID_TABLE | DATA_TABLE | DATA_TABLE_EXT |
*-------------------*------------------*-------------------*
| id n<|>1 id 1<|>n owner_id |
| foreign_id | owner_id | information |
| foreign_id_source | date_field | ... |
| ... | ... | |
*-------------------*------------------*-------------------*
QUERY
SELECT ID_TABLE.foreign_id_source, count(ID_TABLE.id) as count
FROM DATA_TABLE
LEFT JOIN ID_TABLE ON DATA_TABLE.id = ID_TABLE.id
WHERE DATA_TABLE.owner_id = 'some_id'
AND DATA_TABLE.date_field > 'some_date'
AND DATA_TABLE.id IN (
SELECT DATA_TABLE_EXT.owner_id FROM DATA_TABLE_EXT
JOIN DATA_TABLE ON DATA_TABLE_EXT.owner_id = DATA_TABLE.id
WHERE DATA_TABLE.owner_id = 'some_id'
GROUP BY DATA_TABLE.id
HAVING SUM(ABS(DATA_TABLE_EXT.information)) <> 0
)
GROUP BY ID_TABLE.foreign_id_source
ORDER BY count ASC
REQUIRED RESULT
*-------------------*-------------*
| foreign_id_source | count |
*-------------------*-------------*
| source1 | 45 |
| source2 | 10 |
| ... | |
*-------------------*-------------*
Each id in DATA_TABLE may have multiple records in ID_TABLE.
many records in DATA_TABLE may have the same owner_id.
I'm looking for the number of records in data_table with a foreign_id_source, grouped by that foreign_id_source, where the record is after 'some_date' and it's DATA_TABLE_EXT records do not all have a value of 0 in the information field.
Short of creating indexes or other database manipulation is there a way to improve this query in terms of performance?
Any other suggestions are also welcome.
The point is: SUM(ABS(DATA_TABLE_EXT.information)) <> 0 can only be true if at least one DATA_TABLE_EXT.information is non-zero. So we don't have to sum() them, we only only need to check if a non-zero one exists.
[ I don't know if mysql is smart enough to handle the exists(), but in theory it is cheaper, and can be faster]
SELECT it.foreign_id_source, count(it.id) as count
FROM DATA_TABLE dt
LEFT JOIN ID_TABLE it ON dt.id = it.id
WHERE dt.owner_id = 'some_id'
AND dt.date_field > 'some_date'
AND EXISTS (
SELECT *
FROM DATA_TABLE_EXT x
JOIN DATA_TABLE dt2 ON x.owner_id = dt2.id
WHERE x.id =dt.id
AND dt2.owner_id = 'some_id'
AND x.information <> 0
)
GROUP BY it.foreign_id_source
ORDER BY count ASC
;
Often moving the subquery to the FROM will help:
SELECT ID_TABLE.foreign_id_source, count(DATA_TABLE.id) as count
FROM ID_TABLE LEFT JOIN
DATA_TABLE
ON DATA_TABLE.id = ID_TABLE.id JOIN
(SELECT DATA_TABLE.id
FROM DATA_TABLE_EXT JOIN
DATA_TABLE
ON DATA_TABLE_EXT.owner_id = DATA_TABLE.id
WHERE DATA_TABLE.owner_id = 'some_value'
GROUP BY DATA_TABLE.id
HAVING SUM(ABS(DATA_TABLE_EXT.information)) <> 0
) xx
ON DATA_TABLE.id = xx.id
WHERE DATA_TABLE.owner_id = 'some_value' AND
DATA_TABLE.date_field > 'some_date'
GROUP BY x.field1
ORDER BY count ASC;
Then, you can think about indexes. These would be tableX(field2, fieldZ, field1, fieldX), tableI(field1), tableX(field2, field1, fieldB), andtableA(field1)`.

My SQL query is failing to get low cost rooms at multiple rooms bookings

Bellow is my "rooms" mysql table. (This is example table i have created to show here.)
I want to show low cost rooms in case multiple room bookings. Ex: User have select two room, 1 adult in first room and 2 adults in second room. Now my result should come as below
My Query is : SELECT * FROM rooms WHERE hotel_code="100" GROUP BY adult+child Order by cost ASC
It is not working. I am not able to get the expected result with this query. Please let me know the solution for this.
Try this:
SELECT r.*
FROM Rooms r
INNER JOIN
(
SELECT
adult, child, MIN(Cost) MinCost
FROM Rooms
WHERE hotel_code = 100
GROUP BY adult, child
) m ON r.adult = m.adult AND r.child = m.child
AND r.cost = m.mincost
ORDER BY r.id
LIMIT 2;
SQL Fiddle Demo
This will give you:
| ID | HOTEL_CODE | ROOM_TYPE | ADULT | CHILD | COST |
---------------------------------------------------------
| 2 | 100 | Single Delux | 1 | 0 | 20 |
| 3 | 100 | Twin Stadard | 2 | 0 | 25 |
Extending Mahmoud's thinking with a (less scalable) extension...
SELECT a.*
FROM
( SELECT x.*
FROM rooms x
JOIN
( SELECT hotel_code
, adult
, child
, MIN(cost) min_cost
FROM rooms
GROUP
BY hotel_code
, adult
, child
) y
ON y.hotel_code = x.hotel_code
AND y.adult = x.adult
AND y.child = x.child
AND y.min_cost = x.cost
) a
LEFT
JOIN
( SELECT x.*
FROM rooms x
JOIN
( SELECT hotel_code
, adult
, child
, MIN(cost) min_cost
FROM rooms
GROUP
BY hotel_code
, adult
, child
) y
ON y.hotel_code = x.hotel_code
AND y.adult = x.adult
AND y.child = x.child
AND y.min_cost = x.cost
) b
ON b.hotel_code = a.hotel_code
AND b.adult = a.adult
AND b.child = a.child
AND b.cost = a.cost
AND b.id < a.id
WHERE b.id IS NULL;

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets