MySQL - Split SELECT query (WHERE IN) into two rows - mysql

So, I am using MySQL to do a query and have a database like this:
I wanted to do a select query to show every transaction of Bank A and C based on their prefix. This is the expected result:
I have done the query as followed:
SELECT
M.merk AS 'Merk',
COUNT( T.amount ) AS 'Jumlah Transaksi',
SUM( T.amount ) AS 'Total Amount'
FROM
tb_transaksiatm T
INNER JOIN tb_issuer I ON
T.nomor_kartu LIKE CONCAT(I.prefix, '%')
INNER JOIN tb_terminalatm M ON
T.terminal = M.nomor_terminal
WHERE
I.bank IN ('A', 'C') # Declare the Bank;
But my result is not the same as expected. It combined and summed both rows from Bank A and Bank C. This is my result:
The question is, how do I split the WHERE IN condition into two rows? Any help would be appreciated. Thank you.
Note: The language is Indonesian.

SELECT
M.merk AS 'Merk',
COUNT( T.amount ) AS 'Jumlah Transaksi',
SUM( T.amount ) AS 'Total Amount'
FROM
tb_transaksiatm T
INNER JOIN tb_issuer I ON
T.nomor_kartu LIKE CONCAT(I.prefix, '%')
INNER JOIN tb_terminalatm M ON
T.terminal = M.nomor_terminal
WHERE
I.bank IN ('A', 'C') # Declare the Bank
group by M.merk;

When you use an aggregation function such as SUM or COUNT and you do not specify a GROUP BY, it will aggregate all rows together. Fields such as M.Merk that could vary between the rows being aggregated will have a value taken from an arbitrary one of the rows being aggregated, though modern versions of mysql default to an ONLY_FULL_GROUP_BY mode where selecting such a field will result in an error instead of an arbitrary value.
It sounds like you intend to have a GROUP BY I.Bank, M.Merk, though it is confusing that you don't include the bank in your selected fields.

Related

Fetching data from many to many relationship

I am having 3 tables namely
category
case
cost
office_id
category_id
linked to >
category_id
case_number
linked to >
case_number
total_cost
Problem:
I need to fetch the total number of case and their respective cost for every office id which is there in category table
Query I have written:
select cm_c_d.case_number,cm_c.office_id,count(*) as case_count from categories as cm_c
join case as cm_c_d on cm_c.category_id = cm_c_d.category_id
join cost on cm_c_d.case_number = cost.case_number group by office_id;
but I don't think this will provide the desired result as joining all the three tables will increase the row count.
Updated SQL query:
select cm_c.office_id
, count(DISTINCT cm_costs.case_number) as case_count
, SUM(total_charge) AS overall_cost
from cm_categories as cm_c
JOIN cm_case_details as cm_c_d
on cm_c.category_id = cm_c_d.category_id
join cm_costs
on cm_c_d.case_number = cm_costs.case_number
group by cm_c.office_id
;
If you want distinct case count (adjusted with the new table names):
SELECT cm_c.office_id
, COUNT(DISTINCT cm_costs.case_number) AS case_count
, SUM(total_charge) AS overall_cost
FROM cm_categories AS cm_c
JOIN cm_case_details AS cm_c_d ON cm_c.category_id = cm_c_d.category_id
JOIN cm_costs ON cm_c_d.case_number = cm_costs.case_number
GROUP BY cm_c.office_id
;
COUNT(DISTINCT x) means count the distinct number of x values for each group.
Also notice we needed to quote the case table name. As mentioned in prior comments, that's a reserved word. I suggest avoiding use of reserved words as identifiers (table, column, etc names).
I've removed case_number from your SELECT list because it's not functionally dependent on the GROUP BY terms. That means the design/query does not guarantee that there is at most one case_number for each office_id.
If you want a case_number in the select list, you would need to use a form of aggregation (like COUNT), as follows:
SELECT cm_c.office_id
, COUNT(DISTINCT cm_costs.case_number) AS case_count
, SUM(total_charge) AS overall_cost
, MIN(cost.case_number) AS some_case_number
, GROUP_CONCAT(cost.case_number) AS all_cases
FROM cm_categories AS cm_c
JOIN cm_case_details AS cm_c_d ON cm_c.category_id = cm_c_d.category_id
JOIN cm_costs ON cm_c_d.case_number = cm_costs.case_number
GROUP BY cm_c.office_id
;
Be careful of GROUP_CONCAT for very large sets, since that result column width could get rather wide.
Here's the test case, with necessary adjustments:
Test case
For more detail on GROUP BY and functional dependence, see the following links:
group-by-handling
group-by-functional-dependence

using an aggregate value along with non-agrregate values in SQL

I need to use the average value of the column 'sales' from a table called 'previous_target' and compare that value with the individual rows of the same column 'sales' of the same table .
I get the required result when I disable the SQL mode from only_full_group_by.
But i would like to know if there is a better way to write the code without disabling the full group by mode.
Here is an example SQL query.
select
f.id,t.sale as previous_target,
case
when t.sale > 1.25*(avg(t.sale)) then round((avg(t.sale)*1.1),2)
when t.sale < 0.9*(avg(t.sale)) then round((avg(t.sale)*0.9),2)
else
t.sale
end
as current_target from
details f
inner join prev_target t on f.l_number=t.l_number
inner join time_details ft on ft.id=f.id
note:if i add the line
group by f.id,f.l_number,t.sale
it just copies the same value onto the current_target column .
can anyone suggest a way to use the average of the sales column from the prev_target table and compare it with each row of the same table with the given conditions.
I hope I conveyed my requirement without causing much confusion.
SELECT f.id, t.sale AS previous_target,
CASE
WHEN t.sale > 1.25*(a.sale) then round(((a.sale)*1.1),2)
WHEN t.sale < 0.9*(a.sale) then round(((a.sale)*0.9),2)
ELSE
t.sale
END AS current_target
FROM details f
INNER JOIN prev_target t ON f.l_number = t.l_number
INNER JOIN
(
SELECT avg(t.sale) AS sale, t.l_number FROM prev_target t GROUP BY t.l_number
)AS a ON t.l_number = a.l_number
INNER JOIN time_details ft ON ft.id = f.id
Using OVER clause would be best recommended here for aggregation. I have created sample run for you (ofcourse different data and columns) but you will get the gist.
create table sales(amount int);
insert into sales values(5);
insert into sales values(10);
insert into sales values(15);
insert into sales values(20);
insert into sales values(10);
select * from sales;
select avg(amount) average from sales;
select amount, case when amount >= (avg(amount) over (PARTITION BY 1)) then 'Good' else 'Bad' end as type from sales;
Result >>

Display zero in group by sql for a particular period

I am trying to run the following query to obtain the sales for each type of job for a particular period. However for certain months where there are no jobs of a particular job type performed no 0 is displayed in sales.
How can i display the zeros in such a condition.
Here is the sql query-
select Year(postedOn), month(postedOn), jobType, sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
order by jobType, year(postedOn), month(postedOn)
Typically, this is where your all-purpose calendar or numbers table comes in to anchor the query with a consistent sequential set:
SELECT job_summary.*
FROM Calendar
CROSS JOIN (
-- you may not have though about this part of the problem, though
-- what about years/months with missing job types?
SELECT distinct jobType FROM tbl_jobs
) AS job_types
LEFT JOIN (
select Year(postedOn) AS year,month(postedOn) as month,jobType ,sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
) job_summary
ON job_summary.jobType = job_types.jobType
AND job_summary.year = Calendar.year
AND job_summary.month = Calendar.month
WHERE Calendar.day = 1 -- Assuming your calendar is every day
AND calendar.date BETWEEN some_range_goes_here -- you don't want all time, right?
order by job_types.jobType, Calendar.year, Calendar.month

SQL query:Having number=max(number) doesn't work

I have two tables,Writer and Books. A writer can pruduce many books. I want to get the all writers who produce maximal number of books.
Firstly, my sql query is like:
SELECT Name FROM(
SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE
Writer.ID=Book.ID
GROUP BY Writer.Name
)
WHERE NUMBER=(SELECT MAX(NUMBER) FROM
(SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE Writer.ID=Book.ID
GROUP BY Writer.Name
)
It works. However I think this query is too long and there exists some duplications. I want to make this query shorter. So I try another query like this:
SELECT Name FROM(
SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE
Writer.ID=Book.ID
GROUP BY Writer.Name
HAVING NUMBER = MAX(NUMBER)
)
However, this HAVING clause doesn't work and my sqlite says its an error.
I don't know why. Can anyone explain to me ? Thank you!
The HAVING clause provides filtering on the final set (typically after a group by) and does not provide additional grouping functionality. Think of it just like a WHERE clause, but can be applied after a GROUP BY.
Your query with the HAVING NUMBER = MAX(NUMBER) implies grouping of the set of NUMBER values across all records and doesn't make sense in this example (even though we all get what you want it to do).
Each query provides you with one level of aggregation, so you cannot use Max on COUNT in the same query. You need a sub-query like you did in your first query.
However, your first query can be simplified on MySQL to:
SELECT Writer.Name
FROM Writer, Book
WHERE Writer.ID = Book.ID
GROUP BY Writer.Name
HAVING COUNT(Book.ID) = (SELECT COUNT(Book.ID) AS n
FROM Writer, Book
WHERE Writer.ID = Book.ID
GROUP BY Writer.Name
ORDER BY n DESC
LIMIT 1)
In MySQL (but not SQLite), you can use variables to reduce the amount of work and make a simpler query. However, there are nuances there, because variables with group by require an extra level of subqueries:
SELECT name
FROM (SELECT t.*, (#m := if(#m = 0, NUMBER, #m)) as maxn
FROM (SELECT w.Name, COUNT(b.ID) AS NUMBER
FROM Writer w JOIN
Book b
ON w.ID = b.ID
GROUP BY w.Name
) t CROSS JOIN
(SELECT #m := 0) params
ORDER BY NUMBER desc
) t
WHERE maxn = number;
It looks like you are nesting aggregate functions, which is not allowed.
HAVING NUMBER = MAX(NUMBER) is like HAVING COUNT(Book.ID) = MAX(COUNT(Book.ID))
Nesting COUNT inside MAX seems to be the issue here

Mysql Group By and ABS function Stack

I have sql code like this, when I execute the code with GROUP BY it just shows record number one and it doesn't use the ABS function. Anyone can help me please?
SELECT *, `tblLifeAgency`.`AgencyName`, `mlp`.`Basic40`, `mlp`.`ADB40`, `mlp`.`CiAccel40`, `mlp`.`Basic50`, `mlp`.`ADB50`, `mlp`.`CiAccel50`, `mlp`.`Basic60`, `mlp`.`ADB60`, `mlp`.`CiAccel60`
FROM (`tblPackage`)
INNER JOIN `tblMatrixLifePackage` mlp ON `mlp`.`PackageID` = `tblPackage`.`PackageID`
INNER JOIN `tblCompany` ON `tblPackage`.`CompanyID` = `tblCompany`.`CompanyID`
INNER JOIN `tblLifeAgency` ON `tblLifeAgency`.`CompanyID` = `tblCompany`.`CompanyID`
AND `tblPackage`.`IsActive` = '1'
WHERE `tblPackage`.`PackageType` = '2'
GROUP BY tblPackage.CompanyID
ORDER BY abs(tblPackage.TotalPremi - 250000)
GROUP BY groups your records. If you have record values, you have to decides which result to show as a summary for a line. For example, you can take all values in one group as positive (ABS) and sum them (SUM). Then try converting this pattern into your case (to understand better):
SELECT
`group`,
ABS(
SUM(`value`)) AS `sum`
FROM my_table
GROUP BY `group`