SUM() counts row twice and displaying double result - mysql

Why SUM() counts row twice and displaying double of actual result in here ?
Here I m trying to count total rows of status that has 0 value from the inv_id table of each student (inv_id.s_id) .
it has to show 4 based on the row number in inv_lst table but here it is showing 8.
If fee.id is GROUP_BY then it shows actual SUM but same student id starts to duplicate.
Please see fiddle - SQL Fiddle
Database Structure
class
id | ttl
===========
1 | One
2 | Two
section
id | ttl
===========
1 | A
2 | B
fee
id | ttl
===============
1 | Annual
2 | Monthly
student
id | ttl | cls | sec
===========================
1 | John| 1 | 1
2 | Paul| 1 | 1
3 | Rina| 2 | 1
sec_fee
id | c_id| s_id| f_id| fee
===================================
1 | 1 | 1 | 1 | 1000
2 | 2 | 1 | 2 | 560
inv_id
id | s_id| ft_id | status
==================================
1 | 1 | 1 | 0
2 | 1 | 2 | 0
3 | 1 | 3 | 0
4 | 1 | 4 | 0
Mysql
SELECT
student.id, student.ttl AS stdt,
cls.ttl AS cls,
sec.ttl AS sec,
GROUP_CONCAT(DISTINCT fee.id, '.', fee.ttl, '-', sec_fee.fee,'<br/>' ORDER BY sec_fee.f_id) AS amnt,
SUM(inv_id.status=0) AS upad,
SUM(inv_id.status=1) AS pad
FROM
student
JOIN
cls ON cls.id=student.cls
LEFT JOIN
sec ON sec.id=student.sec
LEFT JOIN
inv_id ON inv_id.s_id = student.id
LEFT JOIN
sec_fee ON sec_fee.c_id = student.cls
LEFT JOIN
fee ON fee.id = sec_fee.f_id
WHERE
cls.id = 1

Related

MySQL inner join limit 1 row from second table

I have 2 (MySQL) tables , exchange table can have 1--n rows in exchitems, when an exchange record has multiple rows, I would like to display the word "multi", but when there is only 1 row, I would like to display the row's details:
First table (exchange):
xid (PK) | cusid | xdate | xref | xtotal
1 | 1 | 2021-10-01 | 345667 | 500
2 | 1 | 2021-10-01 | 345668 | 200
3 | 1 | 2021-10-02 | 345669 | 450
4 | 1 | 2021-10-03 | 345670 | 1200
And the second table (exchitems):
chid (PK) | xid | cusid | xcur| xsell| xbuy
1 | 1 | 1 | USD | 300 | 0
2 | 1 | 1 | EUR | 0 | 400
3 | 2 | 1 | USD | 200 | 0
4 | 3 | 1 | EUR | 0 | 500
5 | 4 | 1 | EUR | 0 | 800
6 | 4 | 1 | USD | 300 | 0
The exchange table must have at least 1 row in exchtiems table, and this is what I would like to get:
xid | cusid | xdate | xref | xcur | xsell | xbuy | xtotal
1 | 1 | 2021-10-01 | 345667 | multi | 0 | 0 | 500
2 | 1 | 2021-10-01 | 345668 | USD | 200 | 0 | 200
3 | 1 | 2021-10-02 | 345669 | EUR | 0 | 500 | 450
4 | 1 | 2021-10-03 | 345670 | multi | 0 | 0 | 1200
Using the following query, i am able to get the all records, but I would like to limit the exchitems table to one row "any row" when there are multiple rows, the count is used to display the word "multi" when it is > 1:
SELECT a.xid,a.xdate,a.xref,a.xtotal,b.xcur,b.xsell,b.xbuy,
(SELECT COUNT(*) FROM exchitems c WHERE c.xid= a.xid) AS tRec
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
ORDER BY a.xdate DESC,a.xid DESC
I have tried many different queries but couldn't achieve what I want.
Any help is highly appreciated.
Untested, but this should work.
SELECT
a.xid,
a.cusid,
a.xdate,
a.xref,
-- if distinct currency in the group is > 1 then the word 'multi', else currency.
IF(COUNT(DISTINCT b.xcur) > 1, 'multi', b.xcur) AS `xcur`,
b.xsell,
b.xbuy,
a.xtotal
FROM exchange a
JOIN exchitems b ON a.xid = b.xid AND a.cusid = b.cusid
WHERE a.cusid = 1
GROUP BY xid -- will let you have exchange rows with groups of exchitems 1:n
ORDER BY a.xdate DESC, a.xid DESC
You can modify your current query to the following:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
The result will look like this:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
MIN(b.xsell)
MIN(b.xbuy)
MAX(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
0
0
500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0
0
1200
The part where I use MIN and MAX is according to your expected result. You may want to clarify which value to show there is you have multiple value. If I change that to GROUP_CONCAT:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
GROUP_CONCAT(b.xsell),
GROUP_CONCAT(b.xbuy),
GROUP_CONCAT(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Then you'll see a more elaborate result:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
GROUP_CONCAT(b.xsell)
GROUP_CONCAT(b.xbuy)
GROUP_CONCAT(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
300,0
0,400
500,500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0,300
800,0
1200,1200
To make the xcur value show multi, you probably can do something like:
SELECT a.xid, a.cusid, a.xdate,
CASE WHEN COUNT(b.xcur) > 1 THEN 'multi' ELSE MAX(b.xcur) END AS xcur,
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Demo fiddle

MySQL IFNULL is not returning result

I am trying to fetch remaining quantity of books with following approach
stock = total+(receive-Issued);
Table book_qnt - lists of book Quantiy
id | book | qnt
=======================
1 | 1 | 20
2 | 2 | 12
Table book - lists of Book
id | ttl
===========
1 | Social Experiment
2 | Evolution of Culture
Table book_trns - lists of book transcation
id | bk | std | iss | rcv | dte
==========================================
1 | 2 | 1 | 6 | 0 | 2019-04-11
2 | 2 | 2 | 4 | 0 | 2019-04-05
It is fine for only those books which has receive and issued values.
In DB language, it is displaying result of those books which book_trans.rcv and book_trans.iss is not NULL
SELECT
book.id AS book,
book_qnt.qnt+((SUM(book_trans.rcv))-(SUM(book_trans.iss))) AS stock,
Result
book| stock
===========
1 | NULL
2 | 2
Now I am try to fetch result as following, if book_trans.rcv and book_trans.iss is NULL, then it should fetch total qnt from book_qnt
Desired Result
book| stock
===========
1 | 20 // id 1's book_trans.rcv and book_trans.iss is NULL, so it should show total qnt
2 | 2 // id 2's book_trans.rcv and book_trans.iss is not NULL, so it is calculating
What I do if book_trans.rcv and book_trans.iss row is Null, then assign value as 0.
SELECT
book.id AS book,
book_qnt.qnt+((IFNULL(0, SUM(book_trans.rcv)))-(IFNULL(0, SUM(book_trans.iss)))) AS stock,
But Result (calculation doesn't work)
book| qnt
===========
1 | 20
2 | 12
Full MySQL
SELECT
book_qnt.qnt+((IFNULL(0, SUM(book_trans.rcv)))-(IFNULL(0, SUM(book_trans.iss)))) AS stock,
lib_bk.id,
lib_bk.ttl
FROM
book
JOIN
book_qnt ON book_qnt.book=book.id
LEFT JOIN
book_trans ON book_trans.bk=book.id
GROUP BY book.id
Your 0 replacement in the null test is the wrong way round. Try this
SELECT
book_qnt.qnt , SUM(book_trns.rcv), SUM(book_trns.iss),
((IFNULL(SUM(book_trns.rcv),0))-(IFNULL(SUM(book_trns.iss),0))),
book_qnt.qnt+((IFNULL(SUM(book_trns.rcv),0))-(IFNULL(SUM(book_trns.iss),0))) AS stock,
book.id,
book.ttl
FROM
book
JOIN
book_qnt ON book_qnt.book=book.id
LEFT JOIN
book_trns ON book_trns.bk=book.id
GROUP BY book.id;
+------+--------------------+--------------------+-----------------------------------------------------------------+-------+------+----------------------+
| qnt | SUM(book_trns.rcv) | SUM(book_trns.iss) | ((IFNULL(SUM(book_trns.rcv),0))-(IFNULL(SUM(book_trns.iss),0))) | stock | id | ttl |
+------+--------------------+--------------------+-----------------------------------------------------------------+-------+------+----------------------+
| 20 | NULL | NULL | 0 | 20 | 1 | Social Experiment |
| 12 | 0 | 10 | -10 | 2 | 2 | Evolution of Culture |
+------+--------------------+--------------------+-----------------------------------------------------------------+-------+------+----------------------+
2 rows in set (0.00 sec)
And do try to keep your table names consistent with the query.

MYSQL join query not working as expected

I have basically have two tables:
1) billingcharges : here we store id of a restaurant (restid) , charge id (chargeid), chargetime(timeinmillis when the charge occurred),chargeamount(int amount of the actual charge).charge id is a foreign key to the billingchargedetails table.
2) billingchargedetails: here we store the details of all the possible charges. chargeid(primary key int), chargename (name of the charge), perdaycost (cost per day of the charge)
What i expect:
a summary report of totalamount of charge for each charge for each restaurant.
The current entries inside the tables are:
select * from billingcharges;
+--------+----------+---------------+--------------+
| restid | chargeid | chargetime | chargeamount |
+--------+----------+---------------+--------------+
| 1 | 1 | 1536363636363 | 700 |
| 2 | 1 | 1536363636363 | 500 |
| 1 | 1 | 1568789654123 | 500 |
+--------+----------+---------------+--------------+
select * from billingchargedetails;
+----------+--------------------+------------------+
| chargeid | chargename | chargecostperday |
+----------+--------------------+------------------+
| 1 | Base Charge | 50 |
| 2 | Spotlight Listing | 50 |
| 3 | Gold Notification | 500 |
| 4 | Discount (FIRST50) | 18 |
+----------+--------------------+------------------+
A simple join on chargeid ended up not giving me the qty and sum as expected.so i need some form of a left or right outer join, that much i know and tried
I tried a left join as follows:
select restid, B.chargeid, chargename, count(B.chargeid) as qty,
sum(ifnull(chargeamount,0)) as total
from billingcharges as B
left join billingchargedetails as C on B.chargeid=C.chargeid
group by restid,B.chargeid;
+--------+----------+-------------+-----+-------+
| restid | chargeid | chargename | qty | total |
+--------+----------+-------------+-----+-------+
| 1 | 1 | Base Charge | 2 | 1200 |
| 2 | 1 | Base Charge | 1 | 500 |
+--------+----------+-------------+-----+-------+
This does work and sums things but there are missing charges for each restaurant. even if they arent present inside the billinghcarges ie the left table, i need it with qty 0 and total 0.
I tried a right join and a random value was selected by mysql from the non existing entries inside the left table as follows:
select restid, B.chargeid, chargename, count(B.chargeid) as qty,
sum(ifnull(chargeamount,0)) as total
from billingcharges as B
right join billingchargedetails as C on B.chargeid=C.chargeid
group by restid,B.chargeid;
+--------+----------+-------------------+-----+-------+
| restid | chargeid | chargename | qty | total |
+--------+----------+-------------------+-----+-------+
| NULL | NULL | Spotlight Listing | 0 | 0 |
| 1 | 1 | Base Charge | 2 | 1200 |
| 2 | 1 | Base Charge | 1 | 500 |
+--------+----------+-------------------+-----+-------+
The expected output is something like:
restid chargeid chargename qty totalamount
1 1 Base Charge 2 1200
1 2 Spotlight 0 0
1 3 Gold 0 0
1 4 Discount 0 0
2 1 Base Charge 1 500
2 2 Spotlight 0 0
2 3 Gold 0 0
2 4 Discount 0 0
'same as above expected for each restid in billingcharges'
Before you can do the outer join, you need to generate the cross-product of restaurants to charge types.
Something like the following (but I have not tested it):
SELECT R.restid, D.chargename, COUNT(B.chargeid) AS qty,
SUM(IFNULL(B.chargeamount, 0)) AS total
FROM (SELECT DISTINCT restid FROM billingcharges) AS R
CROSS JOIN billingchargedetails AS D
LEFT JOIN billingcharges AS B ON R.restid=B.restid AND D.chargeid=B.chargeid
GROUP BY R.restid, D.chargename;
In this example, the cross-product of R and D is every restaurant crossed with every charge type.
Of course not all of those charges exist for every restaurant. So the outer join to billingcharges finds those rows that do exist for each respective combination of restaurant & charge type.

left join doesn't return all rows Mysql

I have a table that has greenhouse information
pr_grouper_details
--------------------------------------------------------
id | grouper_detail | status | periphery | id_tenant
--------------------------------------------------------
1 | 1 | 100 | 0 | 1
-------------------------------------------------------
1 | 2 | 100 | 0 | 1
-------------------------------------------------------
1 | 3 | 100 | 0 | 1
-------------------------------------------------------
1 | 4 | 100 | 0 | 1
-------------------------------------------------------
I have this table that has the sowings of each of the greenhouses:
---------------------------------------------------------
id | id_grouper_detail | id_product | type | status
---------------------------------------------------------
1 | 1 | 1 | SW | 100
--------------------------------------------------------
1 | 1 | 2 | SW | 100
--------------------------------------------------------
1 | 2 | 1 | SW | 100
--------------------------------------------------------
1 | 3 | 1 | SW | 100
--------------------------------------------------------
This the table with the information of products:
----------------------------
id | product | status |
----------------------------
1 | FLOWER1 | 100 |
----------------------------
2 | FLOWER2 | 100 |
----------------------------
I need to bring all the greenhouses regardless of whether you have products or not, but only bring me those that have products:
SELECT id, grouper_detail, GROUP_CONCAT(product SEPARATOR ' - ') AS products
FROM(
SELECT pr_grouper_details.id, pr_grouper_details.grouper_detail, pr_products.product
FROM sw_sowing
INNER JOIN pr_products ON pr_products.id = sw_sowing.id_product
LEFT JOIN pr_grouper_details ON pr_grouper_details.id = sw_sowing.id_grouper_detail
AND pr_grouper_details.status = 100
AND pr_grouper_details.periphery = 0
AND pr_grouper_details.id_tenant = 1
WHERE sw_sowing.type = 'SW'
AND sw_sowing.status = 100
GROUP BY pr_grouper_details.id, pr_products.id
) AS s
GROUP BY id
This is the result
----------------------------------------------
id | grouper_detail | products
----------------------------------------------
1 | 1 | FLOWER1 - FLOWER2
----------------------------------------------
2 | 2 | FLOWER1
----------------------------------------------
3 | 3 | FLOWER1
----------------------------------------------
But I need something like this:
----------------------------------------------
id | grouper_detail | products
----------------------------------------------
1 | 1 | FLOWER1 - FLOWER2
----------------------------------------------
2 | 2 | FLOWER1
----------------------------------------------
3 | 3 | FLOWER1
----------------------------------------------
4 | 4 | NULL
----------------------------------------------
I am using LEFT JOIN but it doesn't work, I hope that you can help me!
Your subquery would be much easier to follow with table aliases. You have the filtering conditions correct in the ON clause. However, you are aggregating by pr_grouper_details.id and this could be NULL.
Instead use the equivalent column s.id_grouper_detail. Then, I think you need to start with pr_grouper_details because you want to keep all of those rows. The ONs and WHERE clauses need to be adjusted:
SELECT s.id_grouper_detail, p.product
FROM pr_grouper_details gd LEFT JOIN
sw_sowing s
ON gd.id = s.id_grouper_detail AND
s.type = 'SW' AND
s.status = 100 LEFT JOIN
pr_products p
ON p.id = s.id_product
WHERE gd.status = 100 AND
gd.periphery = 0 AND
gd.id_tenant = 1
GROUP BY s.id_grouper_detail, p.product;
You have gd.id in your original query. That is a bad idea.
Try this one:
SELECT gd.grouper_detail, GROUP_CONCAT(product SEPARATOR ' - ') AS products
FROM pr_grouper_details gd LEFT JOIN
sw_sowing s
ON gd.grouper_detail = s.id_grouper_detail AND
s.type = 'SW' AND
s.status = 100 LEFT JOIN
pr_products p
ON p.id = s.id_product
WHERE gd.status = 100 AND
gd.periphery = 0 AND
gd.id_tenant = 1
GROUP BY s.id_grouper_detail

Group by gives wrong title

I've got this database structure:
id | parent_id | name | num_begin | num_changes
====|===========|============|===========|=============
1 | 1 | 0001-0001 | 10 | 0
2 | 2 | 0001-0002 | 15 | 2
3 | 3 | 0001-0003 | 20 | 0
4 | 3 | testgroup | 5 | 1
The user can make groups and subgroups. The subgroup get's a parent_id. The testgroup in the above table is a subgroup of 3.
Now I want to select the current numbers (num_begin - num_changes). So I made this query:
SELECT
name,
( SUM( num_begin ) - SUM( num_changes ) ) AS num_now
FROM
groups
GROUP BY
parent_id
ORDER BY
id DESC
The result is:
name | num_now
============|===========
0001-0001 | 10
0001-0002 | 13
testgroup | 24
But I would like to have the name of the original group. So instead of testgroup it should be 0001-0003
Any thoughts?
Do a join on parents:
select p.name,
sum(c.num_begin) - sum(c.num_changes) as num_now
from groups c
join groups p on c.parent_id = p.id
group by p.id, p.name
When joining on parent you are getting the following results:
---------------------c-------------------------- | ----------p-------------...
id parent_id name num_begin num_changes | id parent_id name
1 1 0001-0001 10 0 | 1 1 0001-0001
2 2 0001-0002 15 2 | 2 2 0001-0002
3 3 0001-0003 20 0 | 3 3 0001-0003
4 3 testgroup 5 1 | 3 3 0001-0003
No you are grouping by p and calculating c.