I'm performing a query that looks like this:
SELECT a.transactionID,a.customerID,b.value
FROM adjustments a
INNER JOIN change b
on a.transactionID = b.transactionID
and a.event_date = b.event_date
and a.event_id = b.event_id
WHERE comment LIKE 'TRANSFER'
ORDER BY a.transactionID;
this query brings the following result:
transactionID | customerID | value
------------------------------------
TRANSFER-001 | CUSTA | -200
TRANSFER-001 | CUSTB | 200
TRANSFER-002 | CUSTC | -150
TRANSFER-002 | CUSTD | 0
TRANSFER-003 | CUSTA | 0
TRANSFER-003 | CUSTC | 150
I need to change this query to bring a list that ignore those cases where the sum of value is 0 for the same transactionID and also, group the customerID and values as following:
transactionID | customerID_A | value_A | customerID_B | value_B
------------------------------------------------------------------
TRANSFER-002 | CUSTC | -150 | CUSTD | 0
TRANSFER-003 | CUSTA | 0 | CUSTC | 150
Can you give any advise about how to solve this?
Try this :
SELECT * FROM (
SELECT a.transactionID,a.customerID,b.value
FROM adjustments a
INNER JOIN change b
on a.transactionID = b.transactionID
and a.event_date = b.event_date
and a.event_id = b.event_id
WHERE comment LIKE 'TRANSFER'
)M
GROUP BY transactionID,customerID,value
HAVING SUM(value) <> 0
ORDER BY transactionID;
SELECT distinct a.transactionID,a.customerID,b.value
FROM adjustments a
INNER JOIN change b
on a.transactionID = b.transactionID
and a.event_date = b.event_date
and a.event_id = b.event_id
WHERE comment LIKE 'TRANSFER'
ORDER BY a.transactionID;
Try distinct at the beginning it will eliminate all the duplicate values.
If I understand correctly, you want conditional aggregation. However, you need to pivot the customers and there is no column for doing that. You can enumerate the customers for each transaction using variables, and use that to pivot the first two customers on the transaction:
SELECT transactionId,
MAX(CASE WHEN seqnum = 1 THEN customerId END) as customer_A,
MAX(CASE WHEN seqnum = 1 THEN value END) as value_A,
MAX(CASE WHEN seqnum = 2 THEN customerId END) as customer_B,
MAX(CASE WHEN seqnum = 2 THEN value END) as value_B
FROM (SELECT a.transactionID, a.customerID, b.value,
(#rn := if(#t = a.transactionID, #rn + 1,
if(#t := a.transactionID, 1, 1)
)
) as seqnum
FROM adjustments a INNER JOIN
change c
ON a.transactionID = c.transactionID AND
a.event_date = c.event_date AND
a.event_id = c.event_id CROSS JOIN
(SELECT #rn := 0, #t := '') params,
WHERE comment LIKE 'TRANSFER'
ORDER BY a.transactionID, b.value DESC
) t
GROUP BY transactionId
HAVING SUM(value) <> 0;
Related
I have 2 tables say:
Orders:
id | Name | Amount | Date
1 | ABC | 100 | 2020-10-01
2 | XYZ | 200 | 2020-10-01
3 | MNO | 250 | 2020-11-01
Order_details:
id | Item | Qty
1 | A | 2
1 | B | 1
1 | C | 3
2 | X | 1
3 | A | 4
Now I want to fetch the data using the date on which the order was made.
Say if I want to fetch the data of 2020-10-01 the output should be something like this:
id | Name | Amount | Date | Item1 | Qty | Item2 | Qty ...
1 | ABC | 100 | 2020-10-01 | A | 2 | B | 1
2 | XYZ | 200 | 2020-10-01 | X | 1
I tried to fetch it using a subquery, but I was not sure how to print that data.
Thanks in advance!
You can use Conditional Aggregation within Dynamic Pivot Statement which works even for DB version 5.5 :
SET SESSION group_concat_max_len = 18446744073709551615;
SET #sql = NULL;
SET #date = '2020-10-01';
SELECT GROUP_CONCAT(
DISTINCT
CONCAT(
'MAX(CASE WHEN rn = ', rn,' THEN Item END ) AS Item', rn,
', MAX(CASE WHEN rn = ', rn,' THEN Qty END ) AS Qty'
)
)
INTO #sql
FROM (
SELECT *, #rn := IF(#i = id, #rn + 1, 1) AS rn, #i := id
FROM Order_details
JOIN (SELECT #i := 0, #rn := 0) i
ORDER BY id, Item
) od;
SET #sql = CONCAT('SELECT o.id, o.name, o.amount, o.date,',#sql,
' FROM Orders o
JOIN (
SELECT *, #rn := IF(#i = id, #rn + 1, 1) AS rn, #i := id
FROM Order_details
JOIN (SELECT #i := 0, #rn := 0) i
ORDER BY id, Item
) od
ON od.id = o.id
WHERE o.date = "',#date,'"
GROUP BY o.id, o.name, o.amount, o.date
ORDER BY o.id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
where parameter value might be updated within the second line ( SET #date = '2020-10-01'; ) . Btw, the function GROUP_CONCAT() has an upper length limit(for the parameter group_concat_max_len with default value of 1024) that might be updated(upto the max value of 18446744073709551615) for the current session for the cases the table has multiple distinct items, so having lots of columns.
Demo
For a fixed maximum number of items per orders, you can use window functions and conditional aggregation:
select o.*,
max(case when od.rn = 1 then item end) item1,
max(case when od.rn = 1 then qty end) qty1,
max(case when od.rn = 2 then item end) item2,
max(case when od.rn = 2 then qty end) qty2,
max(case when od.rn = 3 then item end) item3,
max(case when od.rn = 3 then qty end) qty3
from orders o
inner join (
selet od.*, row_number() over(partition by id order by item) rn
from order_details od
) od on od.id = o.id
group by o.id
You can extend the select clause with more conditional expressions to handle more than 3 items per order.
Note that window functions are available in MySQL 8.0 only.
I have a list of rows and want to group them by ID and a certain start-value. (Say value=1 in the below-given example)
Group-by value
Set of rows - MySQL & Presto
-----------
ID | value
-----------
A | 1
A | 2
B | 1
B | 2
B | 5
B | 1
B | 2
C | 1
C | 3
C | 4
C | 1
D | 1
D | 8
D | 1
-----------
Expected Output :
-----------
ID | Value
-----------
A | 1,2
B | 1,2,5
B | 1,2
C | 1,3,4
C | 1
D | 1,8
D | 1
-----------
Actual Output :
-----------
ID | Value
-----------
A | 1,2
B | 1,2,5,1,2
C | 1,3,4,1
D | 1,8,1
-----------
Without a real id is really hard. So I introduce rowNumber:
SELECT (#row := #row + 1) AS rowNumber, ID,value
FROM myTable
CROSS JOIN (SELECT #row := 0) AS dummy
Then i add 2 columns with max Value:
SELECT a.*
FROM customTable as a
LEFT OUTER JOIN myTable b ON a.id = b.id AND a.value < b.value
WHERE b.id IS NULL
After this i need to use group_concat(value) and group by.
Group by has 2 condition, id and another custom boolean field:
CASE
WHEN l1.rowNumber <= l2.rowNumber THEN 0
ELSE 1
END
FINAL QUERY:
SELECT ct1.id, group_concat(ct1.value) as Value
FROM (
SELECT (#cnt := #cnt + 1) AS rowNumber, ID, value
FROM myTable
CROSS JOIN (SELECT #cnt := 0) AS dummy
) AS ct1
JOIN (
SELECT a.*
FROM (
SELECT (#row := #row + 1) AS rowNumber, ID, value
FROM myTable
CROSS JOIN (SELECT #row := 0) AS dummy
) AS a
LEFT OUTER JOIN myTable b ON a.id = b.id AND a.value < b.value
WHERE b.id IS NULL
) AS ct2 ON ct2.ID = ct1.id
GROUP BY ct1.id,
CASE
WHEN ct1.rowNumber <= ct2.rowNumber THEN 0
ELSE 1
END
You can test Here.
This only works with MySQL 5.6 or above
I can't get my query to work when a LEFT join doesn't match. I think this is related to the cartesian join. Here's a query that works:
select i.id, z.WeekNum, 0, count(s.id) from my156rowtable as i
left JOIN (select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from
(select #startDate := '2016-04-20') sqlv,
my156rowtable
limit 156 ) z
on 1=1
where i.id = 2 and s.id = 0
group by z.WeekNum, s.id
Which brings back:
| 2 | 201836 | 0 | 1 |
| 2 | 201837 | 0 | 1 |
| 2 | 201838 | 0 | 1 |
| 2 | 201839 | 0 | 1 |
| 2 | 201840 | 0 | 1 |
| 2 | 201841 | 0 | 1 |
but with my addition of stats_to_my156rowtable I get rows 201838 and 201839 missing.
select i.id, z.WeekNum, 0, count(s.id) from my156rowtable as i
left JOIN (select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from
(select #startDate := '2016-04-20') sqlv,
my156rowtable
limit 156 ) z
on 1=1
left join stats_to_my156rowtable as s
on i.id = s.sid and z.WeekNum = YearWeek(s.date)
where i.id = 2 and s.id = 0
group by z.WeekNum, s.id
returns:
| 2 | 201836 | 0 | 8 |
| 2 | 201837 | 0 | 14 |
| 2 | 201840 | 0 | 9 |
if there is no match I'd like the count to be 0. I figured I could this with a case or coalesce later but with no return I'm not finding much luck.
Your condition on the right table in the WHERE clause turn your LEFT JOIN into INNER JOIN. Basically, it filter out rows that doesn't have id = 0, including your NULL rows. Probably, moving the condition to the ON clause will solve your problem.
select i.id, z.WeekNum, 0, count(s.id)
from my156rowtable as i
left JOIN (
select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from (
select #startDate := '2016-04-20'
) sqlv, my156rowtable
limit 156
) z on 1=1
left join stats_to_my156rowtable as s on i.id = s.sid and z.WeekNum = YearWeek(s.date) AND s.id = 0
where i.id = 2
group by z.WeekNum, s.id
friends,
I would like to create a query to return me 2 lines above and 2 lines below the selected ID, but the comparison must be made by the ranking.
Query that creates a league table.
SET #rowId :=0;
SELECT
#rowid:= #rowid + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta;
This query return this:
+---------+-------------------+-------------+------------+
| ranking | nomeTime | totalPontos | FK_loginID |
+---------+-------------------+-------------+------------+
| 1 | Mathemio Greus-SB | 612.90 | 7 |
| 2 | CR Hipotenusa VG | 572.67 | 4 |
| 3 | Zica Danada | 549.20 | 6 |
| 4 | FC LEEDS UNITED | 516.12 | 8 |
| 5 | Bradock F.C | 503.51 | 5 |
+---------+-------------------+-------------+------------+
Try this code. The rankings are derived in two different inline views. Then, they are joined using the ranking (if the second inline view's ranking is -1,=, or +1 than the first inline view's ranking). The data is filtered for the selectedId.
SET #rowId1 :=0;
SET #rowId2 :=0;
SET #selectedId := 8;
SELECT
tabela_ordenada2.*
FROM
(
SELECT
#rowId1 := #rowId1 + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID AS FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta
) tabela_ordenada1
INNER JOIN
(
SELECT
#rowId2 := #rowId2 + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID AS FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta
) tabela_ordenada2
ON (tabela_ordenada2.ranking = tabela_ordenada1.ranking - 1 OR tabela_ordenada2.ranking = tabela_ordenada1.ranking OR tabela_ordenada2.ranking = tabela_ordenada1.ranking + 1)
WHERE tabela_ordenada1.FK_loginID = #selectedId;
Sorry for an unclear question, my english isnt that good. So theres my query:
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
assign
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
GROUP BY statusName,assign
and this is the result:
| ticketID | count | statusName | assign |
|:----------|-----------:|:---------:|:-------:
| 1002 | 2 | open | NULL |
| 1020 | 1 | open | James |
| 1021 | 1 | open | Nick |
| 1015 | 1 | overdue | NULL |
My goal was to count ticket by their status, and if status='open' and assign = null then the status would change to 'unassigned', i need a better solution or just a way to merge the result where 'James' and 'Nick' to be one, as i only need to know whether the ticket is assigned or not.
Don't see where you need department table, think you can remove it safely, but...
SELECT a.statusID, a.Status, Count(*)
FROM(
SELECT statusID,
(CASE WHEN statusName = 'open' and assign IS NULL THEN 'unassigned'
WHEN statusName ='open' and assign IS NOT NULL THEN 'assigned'
ELSE statusName
END) as Status
FROM ticket
INNER JOIN department ON ticket.department = department.departID
INNER JOIN status ON ticket.status = status.statusID
WHERE ticket.department = 100) as a
GROUP BY a.Status, a.statusID
SqlFiddle (simplified)
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
'unassigned'
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
AND assign is NULL
GROUP BY statusName
UNION
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
'assigned'
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
AND assign is NOT NULL
GROUP BY statusName
This query fetches the unassigned and assigned tickets in the form of a union of two disjoint resultsets.
SELECT ticketID,status,sum(CASE status
WHEN 'open' then 1
WHEN '' then 1
WHEN NULL then 1
else 0) as count,statusName,assign
FROM ticket, department, status WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
GROUP BY statusName,assign