MySQL Query trying to (CROSS?) JOIN on one table - mysql

Been trying to figure this out for a couple hours and hoping for some expert assistance:
I have a single Mysql table with data such as:
Date version amount
2021-03-01 A 100
2021-03-02 A 35
2021-03-02 B 80
2021-03-03 A 7
2021-03-03 B 90
2021-03-03 C 3
2021-03-03 A 8
2021-03-04 B 15
2021-03-04 C 90
2021-03-04 B 10
And trying to get output for each version for every day, with amount populated as '0' for null;
Result:
Date version SUM(amount)
2021-03-01 A 100
2021-03-01 B 0
2021-03-01 c 0
2021-03-02 A 35
2021-03-02 B 80
2021-03-02 C 0
2021-03-03 A 15
2021-03-03 B 90
2021-03-03 C 3
2021-03-04 A 0
2021-03-04 B 25
2021-03-04 C 90
I tried various 'JOIN', 'LEFT JOIN' and 'CROSS JOIN' permutations without success.
SELECT distinct c1.date, c2.version
FROM crash_log c1
LEFT OUTER JOIN crash_log c2 ON c1.date = c2.date
GROUP BY c1.date, c2.version
(not even messing with the SUM, just trying to get all the rows with this one)
For now, I have a script that does this by brute force: gets DISTINCT date, then get DISTINCT version, then do a nested loop and build an array for each combination. One trouble is it's not scalable and seems the web connection is timing out before the process finishes on a large set.
I'm thinking there's one (semi-?) efficient query that can do this, but I haven't been able to figure it out.

Write subqueries to get all the dates and versions. Cross join these to get every combination.
Then left join that with the table to get either the actual value or default to 0 when NULL.
SELECT d.date, v.version, IFNULL(c.sum, 0) AS sum
FROM (
SELECT DISTINCT date
FROM crash_log) AS d
CROSS JOIN (
SELECT DISTINCT version
FROM crash_log) AS v
LEFT JOIN (
SELECT date, version, SUM(amount) AS sum
FROM crash_log
GROUP BY date, version) AS c ON d.date = c.date AND v.version = c.version
ORDER BY d.date, v.version

Just like your script, but in SQL.
Cross join the distinct dates to the distinct versions and left join to the table and finally aggregation:
SELECT d.Date, v.version, COALESCE(SUM(t.amount), 0) sum_amount
FROM (SELECT DISTINCT Date FROM tablename) d
CROSS JOIN (SELECT DISTINCT version FROM tablename) v
LEFT JOIN tablename t
ON t.Date = d.Date AND t.version = v.version
GROUP BY d.Date, v.version

Related

Using group_concat With a Condition: Display 0 if Values are Not Equal

I just want to display column fields horizontally but also putting a condition to it. Display zero if it has not met the condition.
Example problem: Find the PAYCODE 912 and 686 and display the amount, if not available, display 0
my_table
EMPLOYEE
PAYCODE
AMOUNT
1
912
1
1
123
3
2
912
5
2
686
7
3
111
4
Output must be:
EMPLOYEE
AMOUNT
1
1,0
2
5,7
3
0,0
My code so far:
SELECT
EMPLOYEE,
GROUP_CONCAT(DISTINCT CONCAT(
IF(PAYCODE = '912', AMOUNT, '0'),
IF(PAYCODE = '686', AMOUNT, '0'))
SEPARATOR',') as AMOUNT
FROM
my_table
Note: There are no duplicate paycodes on a similar employee. e.g. two 912 paycodes
I'm thinking a cross join approach should work here:
SELECT e.EMPLOYEE,
GROUP_CONCAT(COALESCE(t.AMOUNT, 0) ORDER BY e.PAYMENTTYPE DESC) AS AMOUNT
FROM (SELECT DISTINCT EMPLOYEE FROM my_table) e
CROSS JOIN (SELECT '912' AS PAYMENTTYPE UNION ALL SELECT '686') p
LEFT JOIN my_table t
ON t.EMPLOYEE = e.EMPLOYEE AND
t.PAYMENTTYPE = p.PAYMENTTYPE
GROUP BY e.EMPLOYEE;
The cross join between the e and p subqueries generates all employee/payment type combinations of interest (only types 912 and 686). We then left join to your table to bring in the amounts, which if are missing we report 0 instead.

SQL Sum Calculation Confusion

I am new with mysql and working to change a store application to make it have two stock. I created a table to store stock quantity:
Then I plan to create a view with stock quantity, per store, per SKU. I using the following query:
SELECT
`stockList`.`sku`,
SUM(A.`stockQty`) AS 'store1',
SUM(B.`stockQty`) AS 'store2',
SUM(`stockList`.`stockQty`) AS 'total'
FROM `stockList`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=1
) AS A
ON `stockList`.`sku`=A.`sku`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=2
) AS B
ON `stockList`.`sku`=B.`sku`
GROUP BY `stockList`.`sku`
Per resulting table, calculation is not proper and I could not identify the logic:
SKU 43 should show for store1 = 9 and for store2 = 10, total = 19. This is what they show if I execute the select queries alone. Please, let me know if I misunderstood how this sum logic works.
You might to use SUM on subquery to calculate Totle price by sku
LEFT JOIN may make some fields not match causing NULL so use IFNULL to preset value 0
You can try this.
SELECT
T.sku,
SUM(T.stockQty) as totle,
IFNULL(A.`store1`,0) AS `store1`,
IFNULL(B.`store2`,0) AS `store2`
FROM `stockList` AS T
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store1`
FROM `stockList`
WHERE `idStock`=1
GROUP BY sku
) as A ON A.sku = T.sku
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store2`
FROM `stockList`
WHERE `idStock`=2
GROUP BY sku
) AS B ON T.sku =B.sku
GROUP BY T.sku
sqlfiddle
Your query is much more complicated than it needs to be. You can just do this:
SELECT
sku,
SUM(stockQty) as total,
SUM(IF(idStock=1,stockQty,0)) AS `store1`,
SUM(IF(idStock=2,stockQty,0)) AS `store2`
FROM `stockList`
GROUP BY sku
Output:
sku total store1 store2
36 10 10 0
37 3 3 0
38 4 4 0
39 3 3 0
40 10 10 0
41 12 12 0
42 12 12 0
43 19 9 10

Get product total sales per moth, with 0 in the gaps

I have been stuck in a recent problem with a SQL Query. What I'm trying to archieve is to get each product in the store and show how many of them has been sold each month. However, sometimes there are some months where these products were not sold, which means they won't be displayed.
For instance, this is the result I'm getting right now
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 12 122
CN140186 1 10
CN140186 4 2
While I want to get something more like this
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 8 0
CN140027 9 0
CN140027 10 0
CN140027 11 0
CN140027 12 122
CN140186 1 10
CN140186 2 0
CN140186 3 0
CN140186 4 2
And here is the query I'm using at the moment
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
What am I missing? Or what am I doing wrong? Any help is really appreciated.
Thanks in advance.
EDIT:
Additional information:
office_calendar is the calendar table. It only contains the months as registry, from 1 to 12.
Additionally, I'm taking the article/product ID from a table called 'store_shop_korb', which contains all the lines of a made order (so it contains the article ID, its price, the quantity for each order..)
This works for me:
SELECT k.artikelnr, c.datefield AS `Month`, COALESCE(s.Quantity, 0) AS Sold
FROM (
SELECT artikelnr
FROM store_shop_korb
GROUP BY artikelnr
) k
JOIN office_calendar c
LEFT JOIN (
SELECT artikelnr, MONTH(date_insert) AS monthfield, SUM(menge) AS Quantity
FROM store_shop_korb
GROUP BY artikelnr, MONTH(date_insert)
) s ON k.artikelnr = s.artikelnr AND c.datefield = s.monthfield
ORDER BY k.artikelnr, c.datefield
If you have a table of articles, you can use it in the place of subquery k. I'm basically normalizing on the fly.
Explanation:
There's basically 3 sets of data that get joined. The first is a distinct set of articles (k), the second is a distinct set of months (c). These two are joined without restriction, meaning you get the cartesian product (every article x every month). This result is then left-joined to the sales per month (s) so that we don't lose 0 entries.
Add another where condition , i think it will solve your problem
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE IFNULL(SUM(k.menge),0)>0 AND k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
I have tried this in MSAccess and it seems to work OK
SELECT PRODUCT, CALENDAR.MONTH, A
FROM CALENDAR LEFT JOIN (
SELECT PRODUCT, MONTH(SALEDTE) AS M, SUM(SALEAMOUNT) AS A
FROM SALES
WHERE SALEDTE BETWEEN #1/1/2015# AND #12/31/2015#
GROUP BY PRODUCT, MONTH(SALEDTE) ) AS X
ON X.M = CALENDAR.MONTH
If you already have a calender table then use this.
SELECT B.Article,
A.Month,
COALESCE(c.Sold, 0)
FROM (SELECT DISTINCT Months.datefield --Considering this as months feild
FROM office_calendar AS Months) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article
Else you need a months table. Try this.
SELECT *
FROM (SELECT 1 AS month UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article

MySQL nested, nested subquery not getting outter variable

I have a spendings table and a dates table, that are joined by date_id and id...
What I'm trying to do, is get from 1 query all the info from spendings, plus the sum of all the spendings but with a limit and/or offset
This is the query right now
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
( SELECT SUM(sum_table.price)
FROM (
SELECT s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) as sum_table
) AS sum_price
FROM spendings, dates, users, currencies
WHERE spendings.date_id = dates.id
AND day(dates.date) = 25
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
LIMIT 2 OFFSET 0
Output
id price title date username value sum_price
3 6.00 title1 2013-11-25 alex € 21.00
4 15.00 title2 2013-11-25 alex € 21.00
It works, but only if the date here day(d.date) = 25 is the same as the outer one here day(dates.date) = 25
If instead I put day(d.date) = day(dates.date) which seems the logic thing to do, I get #1054 - Unknown column 'dates.date' in 'where clause'
If anyone has an idea to make this simpler let me know :)
Try to join instead of using nested correlated subqueries:
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
y.sum_price
FROM spendings, dates, users, currencies
JOIN (
SELECT day, SUM(sum_table.price) As sum_price
FROM (
SELECT day(d.date) As day,
s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) sum_table
GROUP BY day
) y
ON y.day = day(dates.date)
WHERE spendings.date_id = dates.id
-- AND day(dates.date) = 25 <== commented since it's redundant now
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
Some remarks:
Using old join syntax with commas is not recommended: FROM table1,table2,table2 WHERE
The recommended way of expressing joins is "new" ANSI SQL join syntax:
FROM table1
[left|right|cross|[full] outer|natural] JOIN table2 {ON|USING} join_condition1
[left|right|cross|[full] outer|natural] JOIN table3 {ON|USING} join_condition2
....
Actually this "new syntax" is quite old now, since is has been published, as I remember, in 1992 - 22 years ago. In IT industry 22 years is like 22 ages.

Join and Group by Clause gives wrong output

I have two table L with columns (Code, Qtr, Fy, Limit) and R with (Code, Qtr,Fy,Limit). I want to get sum of limit of left and right table group by code, Qtr an Fy
The following query runs with no error but gives wrong output, can anyone help me in getting right output. IF I use only one table it works fine. I guess problem is with join
select L.Code, L. Qtr, L.FY, sum(L.limit),sum(R.Limit)
from tbl L,tbl R Where
L.Code=R.Code AND
L.Qtr=R.Qtr AND
L.FY=R.FY
group by L.Code,L.Qtr,L.FY
Sample Data ( the table contains other column as well but here i m keeping only selected)
Tbl L
Code Qtr, Fy Limit
001 1 70 200
001 1 70 700
001 2 70 500
001 2 70 300
Table R
Code Qtr Fy Limit
001 1 70 1000
001 1 70 200
001 2 70 50
001 2 70 125
Result
Code Qtr Fy Sum(l.Limit) sum(R.Limit)
001 1 70 900 1200
001 2 70 800 175
I m Using Mysql
Try this query:
select code, qtr, fy, sum(lsum), sum(rsum)
from (
select L.Code, L.Qtr, L.FY, L.limit as lsum, 0 as rsum
from L
union all
select R.Code, R.Qtr, R.FY, 0 as lsum, R.limit as rsum
from R) as combined
group by code, qtr, fy
Using join in this case would be a wrong idea because it will create multiple records (one for each match between L and R) and then when you do a sum you get incorrect results.
The problem is indeed the join - specifically, you're running into problems because you are using a GROUP BY after the join, when the join criteria results in non-unique rows. Usually, the way to solve this is to group before the join:
SELECT L.code, L.qtr, L.fy, L.lim as L_lim, R.lim as R_lim
FROM (SELECT code, qtr, fy, SUM(lim) as lim
FROM L
GROUP BY code, qtr, fy) L
JOIN (SELECT code, qtr, fy, SUM(lim) as lim
FROM R
GROUP BY code, qtr, fy) R
ON R.code = L.code
AND R.qtr = L.qtr
AND R.fy = L.fy
(have a working SQL Fiddle example)
Note that this will only show results for rows that are in both tables. Also, LIMIT is a reserved word (in MySQL and some other RDBMSs), so you're better off avoiding that for a column name.