Maybe my google fu is off.
I have a table: sub_transfer_jobs
job_date mo_id sub_quantity shift_id
2017-05-16 24581 12 1
2017-05-16 86122 8 2
etc.
Another table: mo_numbers
mo_id customer
24581 cust1
86122 cust2
68515 cust1
etc.
I have another table: Calendar.
This just has a date value for every date
What i need to get is a string list of amounts per day per customer
example
2017-05-15 cust1 50
2017-05-15 cust2 0
2017-05-16 cust1 22
2017-05-16 cust2 10
etc
I was going to get a distinct list of customers based one what customers are seen based on a date range
This is where i am but have a problem on the sub query
SELECT `sub_transfer_jobs`.`sub_quantity`, `sub_transfer_jobs`.`job_date`, `sub_transfer_jobs`.`shift_id`, `mo_numbers`.`customer`
FROM `sub_transfer_jobs`
join mo_numbers on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
right join calendar on `calendar`.`datefield` = `sub_transfer_jobs`.`job_date`
right join (
select DISTINCT `mo_numbers`.`customer` from sub_transfer_jobs
join `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
where job_date > '2017-04-15'
) as customerList on customerList.customer = mo_numbers.customer
where job_date > '2017-04-15'
group by `mo_numbers`.`customer`, `sub_transfer_jobs`.`job_date`
EDIT: Correct sql to get what I needed (grouped for comma seperated)
select basecustomer, GROUP_CONCAT(IFNULL(customerdaytotals.total, 0) ORDER BY datefield ASC) AS total FROM
(select basecustomer, datefield from
(select distinct `mo_numbers`.`customer` as basecustomer
FROM `sub_transfer_jobs`
JOIN `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
where `sub_transfer_jobs`.`job_date` > '2017-04-01') as used_customers
CROSS JOIN (
SELECT `calendar`.`datefield` from `calendar`
WHERE `calendar`.`datefield` > '2017-04-01' AND `calendar`.`datefield` < '2017-05-11'
) as daterange) as basedata
LEFT JOIN (
select `sub_transfer_jobs`.`job_date`, `mo_numbers`.`customer`, sum(`sub_transfer_jobs`.`sub_quantity`) as total
FROM `sub_transfer_jobs`
JOIN `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
GROUP BY `sub_transfer_jobs`.`job_date`, `mo_numbers`.`customer`
) as customerdaytotals on customerdaytotals.job_date = basedata.datefield and customerdaytotals.customer = basedata.basecustomer
GROUP BY basecustomer
Which gives my result of
cust1 0,1857,1262,1166,517,1551,0,0,1469,1670,400,0,0,0,...
cust2 0,123,7,0,7,0,0,0,0,0,0,0,0,0,0,84,70,9,53,3,0,0,4...
cust3 0,0,75,425,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0...
cust4 0,0,41,36,44,26,0,0,0,41,0,0,0,0,0,16,88,12,0,0,0,...
cust5 0,277,552,433,280,491,0,0,124,880,1269,0,0,0,0,495...
cust6 0,255,124,620,184,129,0,0,309,103,88,0,0,0,0,118,2...
cust7 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,...
cust8 0,209,123,15,84,296,0,0,296,15,262,0,0,0,0,301,200,...
You need to use CROSS JOIN to get all combinations of customers and dates. Then LEFT JOIN that with a query that gets each customer's total for each date.
SELECT m.date, c.customer, IFNULL(t.total, 0) AS total
FROM mo_numbers AS m
CROSS JOIN customer AS c
LEFT JOIN (
SELECT date, mo_id, SUM(quantity) AS total
FROM mo_numbers
GROUP BY date, mo_id
) AS t ON m.mo_id = t.mo_id AND c.mo_id = t.mo_id
Related
I have my main table employee_rates_year:
tier skill rate year
1 28 110 2019
2 28 101 2019
my column skill is connected to an employees table and subcontractors table, for simplicity here is a shortened version of tblEmployees:
EMPLOYEEID Skill FullName
1 28 Employee One
2 28 Employee Two
and tblSubcontractors:
SUBCONTRACTORID Skill FullName
1 28 Sub One
2 28 Sub Two
I am trying to get a tier, rate and year show up for every individual employee & subcontractor.
I've tried using different joins but nothing seems to give me information with every employee/subcontractor.
Here is my query:
SELECT *
FROM employee_rates_year
left join tblsubcontractors on employee_rates_year.skill = tblsubcontractors.skill
left join tblemployees on employee_rates_year.skill = tblemployees.skill
The problem is that my query shows results like this:
tier skill rate year tblEmployees.FullName tblSubcontractors.Fullname
1 28 110 2019 Employee One Sub One
2 28 101 2019 Employee Two Sub Two
Whereas I am trying to get:
tier skill rate year tblEmployees.FullName tblSubcontractors.FullName
1 28 110 2019 Employee One
1 28 110 2019 Sub One
2 28 101 2019 Employee Two
2 28 101 2019 Sub Two
I have also tried using WHERE to try and differentiate between the two tables and pick individual records.
you should never use SELECT *, because.... 😜
Try:
SELECT
tier,
skill,
rate,
year,
tblEmployees.FullName As Employee
'' As Subcontractor
FROM employee_rates_year
left join tblsubcontractors on employee_rates_year.skill = tblsubcontractors.skill
union all
SELECT
tier,
skill,
rate,
year,
'' As Employee
tblSubcontractors.Fullname As Subcontractor
FROM employee_rates_year
left join tblemployees on employee_rates_year.skill = tblemployees.skill
You seem to need UNION ALL and not a double join:
SELECT e.*, t.FullName EmployeesFullName, NULL SubcontractorsFullName
FROM employee_rates_year e
left join tblsubcontractors t on e.skill = t.skill
UNION ALL
SELECT e.*, NULL, t.FullName
FROM employee_rates_year e
left join tblemployees t on e.skill = t.skill
ORDER BY e.tier, e.skill
Each query of the 2 uses a join of employee_rates_year with 1 of the other tables.
Finally the 2 resultsets are merged with UNION ALL.
Think the problem (or part of the problem) is because of the "left join", which creates one row in result for each row in the "left" table. Since employee_rates_year only has two rows, your result for query will only have 2 rows.
Use a union all to bring the employees and subcontactors together and then join and group by. To get values in "vertical lists", you can use row_number():
select er.skill, er.rate, er.year,
max(e_fullname) as e_fullname,
max(s_fullname) as s_fullname
from ((select e.skill, e.fullname as e_fullname, null as s_fullname,
row_number() over (partition by e.skill order by e.fullname) as seqnum
from tblemployees e
) union all
(select s.skill, null as e_fullname, s.fullname as s_fullname,
row_number() over (partition by s.skill order by s.fullname) as seqnum
from tblsubcontractors s
)
) es left join
employee_rates er
on es.skill = er.skill
group by er.skill, er.rate, er.year, seqnum;
If you just want the values in separate lists (with NULLs), then you don't need the row_number() trick:
select er.skill, er.rate, er.year, e_fullname, s_fullname
from ((select e.skill, e.fullname as e_fullname, null as s_fullname
from tblemployees e
) union all
(select s.skill, null as e_fullname, s.fullname as s_fullname
from tblsubcontractors s
)
) es left join
employee_rates er
on es.skill = er.skill;
I have 2 tables voucher and ledger the fields are
Ledger:
id total_sale cancel_amount date
1 3000 0 2018-01-20
2 3000 0 2018-01-29
3 5000 0 2018-01-30
4 10000 500 2018-01-30
5 2000 100 2018-01-31
6 2000 0 2018-01-31
voucher:
id expenditure date
1 500 2018-01-20
2 800 2018-01-30
3 1000 2018-01-30
4 200 2018-01-31
5 300 2018-01-31
I want a result like[ date between 2018-01-29 to 2018-01-31]
date total_sale total_expenditure
2018-01-29 3000 0
2018-01-30 15000 1800
2018-01-31 4000 500
Please someone help
For such requirements, always prefer a dimension table. Then you'll never get confused on joining two dissimilar tables witch no keys in common.
Tables:
create schema test;
create table date_dim(date date);
insert into date_dim values ('2018-01-20'),
('2018-01-21'),
('2018-01-22'),
('2018-01-23'),
('2018-01-24'),
('2018-01-25'),
('2018-01-26'),
('2018-01-27'),
('2018-01-28'),
('2018-01-29'),
('2018-01-30'),
('2018-01-31');
create table ledger(id int,total_sale int, cancel_amount int, date date);
insert into ledger values
(1,3000,0,'2018-01-20'),
(2,3000,0,'2018-01-29'),
(3,5000,0,'2018-01-30'),
(4,10000,500,'2018-01-30'),
(5,2000,100,'2018-01-31'),
(6,2000,0,'2018-01-31');
create table voucher(id int, expenditure int, date date);
insert into voucher values
(1,500,'2018-01-20'),
(2,800,'2018-01-30'),
(3,1000,'2018-01-30'),
(4,200,'2018-01-31'),
(5,300,'2018-01-31');
SQL Script (for solution):
select l.date,total_sale,
total_expenditure
from
(select d.date date,sum(total_sale)total_sale
from ledger l right join date_dim d on l.date=d.date
where d.date between '2018-01-29' and '2018-01-31'
group by 1)l
join
(select d.date date,sum(expenditure)total_expenditure
from voucher v right join date_dim d on v.date=d.date
where d.date between '2018-01-29' and '2018-01-31'
group by 1)v
on l.date=v.date
group by 1,2,3;
Resultset:
date total_sale total_expenditure
2018-01-29 3000 (null)
2018-01-30 15000 1800
2018-01-31 4000 500
Check the solution at SQL Fiddle
You're hoping to present three different aggregates as columns of your result set.
distinct dates
net sales (total less canceled) (I guess that's what you want.)
expenditures.
You need to create three subqueries and join them.
The dates: (http://sqlfiddle.com/#!9/666dcb/1/0)
SELECT DISTINCT DATE(date) date FROM Ledger
UNION
SELECT DISTINCT DATE(date) date FROM Voucher
The net sales: (http://sqlfiddle.com/#!9/666dcb/2/0)
SELECT DATE(date) date,
SUM(total_sale) - SUM(cancel_amount) total_sale
FROM Ledger
GROUP BY DATE(date)
The expenditures: (http://sqlfiddle.com/#!9/666dcb/3/0)
SELECT DATE(date) date,
SUM(expenditure) total_expenditures
FROM Voucher
GROUP BY DATE(date)
Then you need to join them together on date. (http://sqlfiddle.com/#!9/666dcb/5/0)
SELECT d.date, s.total_sale, e.total_expenditures
FROM (
SELECT DISTINCT DATE(date) date FROM Ledger
UNION
SELECT DISTINCT DATE(date) date FROM Voucher
) d
LEFT JOIN (
SELECT DATE(date) date,
SUM(total_sale) - SUM(cancel_amount) total_sale
FROM Ledger
GROUP BY DATE(date)
) s ON d.date = s.date
LEFT JOIN (
SELECT DATE(date) date,
SUM(expenditure) total_expenditures
FROM Voucher
GROUP BY DATE(date)
) e ON d.date = e.date
WHERE d.date >= somedate AND d.date <= anotherdate
ORDER BY d.date
Why do the first subquery--the one with the dates? It makes sure you get a row in your final result set for dates that don't have any sales or any expenditures.
Why do separate subqueries? Because you want to end up joining three virtual tables--three subqueries--that have either zero or one row per date. If you join tables with more than one row per date, you'll get combinatorial explosion of sales and expenditures, which will exaggerate your sums.
Why use DATE(date)? Because that will allow the date columns in your detail tables to contain date/time values in case you want that.
select l.dates,l.total_sale,
v.total_expenditure
from
(select l.dates dates,sum(total_sale)total_sale
from ledger l
where l.dates between '2018-01-29' and '2018-01-31'
group by l.dates)l
left join
(select v.dates,sum(expenditure)total_expenditure
from voucher v
where v.dates between '2018-01-29' and '2018-01-31'
group by v.dates)v
on l.dates=v.dates;
I have a query that manages attendance. I wanted to add a feature that shows who's paid up for the month and who isn't. It almost works.
SELECT
students.sid,
students.name,
students.day,
students.times,
students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments
ON students.sid = payments.sid
WHERE
students.Active = 'Yes' AND
students.day LIKE 'Tue%' AND
payments.date = (SELECT date FROM payments WHERE students.sid = payments.sid AND
payments.payfor = 'tuition' ORDER BY payments.pid DESC LIMIT 1 )
The main trouble part is the (SELECT date FROM)
subquery. The CONCAT is grabbing the first value it gets from the payments table before the payments.payfor limiter in the payments subquery sets. So, in the table snippet below, I get the sk1 entry, instead of the correct tuition for Dec
pid sid amt payfor forMonth year date
1076 69 7000 tuition Dec 2017 2017-12-17
1074 69 4000 sk1 Sep 2017 2017-12-17
1046 69 7000 tuition Nov 2017 2017-11-23
Is there a way to get that payments.date subquery before or with the CONCAT(payments.year,'-', payments.forMonth)?
You should move the condition related to left join tables in the related on clause otherwise if in where condition work as inner join and don't return he related rows
SELECT students.sid, students.name, students.day, students.times, students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments ON students.sid = payments.sid
AND payments.date = (SELECT date
FROM payments
WHERE students.sid = payments.sid
AND payments.payfor = 'tuition'
ORDER BY payments.pid DESC LIMIT 1
)
WHERE students.Active = 'Yes'
AND students.day LIKE 'Tue%'
and if you want only the tuition you should filter for this value too
SELECT students.sid, students.name, students.day, students.times, students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments ON students.sid = payments.sid
AND payments.date = (SELECT date
FROM payments
WHERE students.sid = payments.sid
AND payments.payfor = 'tuition'
ORDER BY payments.pid DESC LIMIT 1
)
AND payments.payfor='tuition'
WHERE students.Active = 'Yes'
AND students.day LIKE 'Tue%'
OK, I got it. I added
AND payments.payfor = 'tuition'
after the
students.Active = 'Yes' AND
students.day LIKE 'Tue%'
in my original query. Then, everything worked as needed.
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
I have the following tables:
table part_list:
part_number | description | type
100 blablabla blabla
table part_list_supplier:
part_id | artikel
100 100100
100 200100
and I have this query:
select part_list.part_number, part_list.description, part_list.type, group_concat(coalesce(part_list_supplier.artikel, "nothing")) as "artikel"
from part_list
left join part_list_supplier on (part_list.part_number = part_list_supplier.part_id)
group by part_list.part_number;
this is the result:
part_number | description | type | artikel
100 blablablabla blabla 100100,200100
but I want to show the total stock per partnumber behind it. table receipt:
Number | import
100 5
100 10
table sales:
Number | sold
100 5
this is my query for one table:
SELECT SUM(sold) AS sold
FROM sales WHERE number = '".$partnumber.”'
but I want to calculate the stock per number and that must be shown behind the other results.
the full result:
part_number | description | type | artikel | stock
100 blablablabla blabla 100100,200100 10
The stock should be 10 because the total number of imports is 15 (5 + 10) and the total number of sales is 5.
I broke this up into pieces to solve it. I started by writing two queries, one that counted total receipt and one that counted total sales:
SELECT r.number, SUM(r.import) AS totalIn
FROM receipt r
GROUP BY r.number;
SELECT s.number, SUM(s.sold) AS totalOut
FROM sales s
GROUP BY s.number;
Then, I used those as two subqueries of a join to get the stock:
SELECT r.number, totalIn - totalOut AS stock
FROM(
SELECT r.number, SUM(r.import) AS totalIn
FROM receipt r
GROUP BY r.number) r
JOIN(
SELECT s.number, SUM(s.sold) AS totalOut
FROM sales s
GROUP BY s.number) s ON s.number = r.number;
Once I verfied this gave the proper stock, I was able to include those subqueries into your original query to build this:
SELECT pl.part_number, pl.description, pl.type,
GROUP_CONCAT(COALESCE(pls.artikel, "Nothing.")) AS artikel,
r.totalIn - s.totalOut AS stock
FROM part_list pl
LEFT JOIN part_list_supplier pls ON pls.part_id = pl.part_number
JOIN(
SELECT number, SUM(import) AS totalIn
FROM receipt
GROUP BY number) r ON r.number = pl.part_number
JOIN(
SELECT number, SUM(sold) AS totalOut
FROM sales
GROUP BY number) s ON s.number = r.number
GROUP BY pl.part_number;
Here is an SQL Fiddle example.
I may not be understanding your question properly, but can't you just add sum(sales.sold) to your select statement and join the sales table? E.g.:
select part_list.part_number, part_list.description, part_list.type, group_concat(coalesce(part_list_supplier.artikel, "nothing")) as "artikel", sum(sales.sold)
from part_list
left join part_list_supplier on (part_list.part_number = part_list_supplier.part_id)
left join sales on (part_list.part_number = sales.number
group by part_list.part_number;