Add up values for employees in three mysql tables - mysql

I have a very simple problem I am trying to solve but cannot wrap my head around it.
I have three tables of identical structure
t1.id, t1.cust_id, t1.name, t1.value
t2.id, t2.cust_id, t2.name, t2.value
t3.id, t3.cust_id, t3.name, t3.value
Customers appear in some tables but not in others; the 'value' record in each is a dollar amount.
I would like to run a query in mySQL that produces a summation table that adds up all the purchases made by each customer in the three tables.
My desired output would look something like:
Name Customer ID T1 T2 T3
Joe 88888 12.45 45.90 2.34
Ted 99999 8.90 3.45 null
Sue 12123 9.45 2.45 null
I've tried a few queries with JOINs but with no satisfactory results.
Thanks your help!

Use union all to combine the rows from 3 tables and then use aggregation.
select cust_id,name,sum(t1val),sum(t2val),sum(t3val)
from (
select id, cust_id, name, value as t1val, null as t2val, null as t3val from t1
union all
select id, cust_id, name, null, value, null from t2
union all
select id, cust_id, name, null, null ,value from t3
) t
group by cust_id,name

You can do it with SELECT, e.g.:
SELECT (
(SELECT COALESCE(SUM(value),0) FROM t1 WHERE cust_id = 100)
+
(SELECT COALESCE(SUM(value),0) FROM t2 WHERE cust_id = 100)
+
(SELECT COALESCE(SUM(value),0) FROM t3 WHERE cust_id = 100)
) as total;
Here's the SQL Fiddle.

Related

How join statements execute in sql

I'm trying to fetch the data from user table such that every row contains date value(not null). If value is null then it should be view that column with a date of id of above date which have same id.
Without updating the table rows, only with select statement?
Here is the table
NAME, DATE, ID
A, 2021-01-21, 1
B, null, 1
C, null, 1
D, 2021-01-18, 2
D, null, 2
It should be viewed like
A, 2021-01-21, 1
B, 2021-01-21, 1
C, 2021-01-21, 1
D, 2021-01-18, 2
D, 2021-01-18, 2
Now the query I think is =>
select t1.name, t2.date ,t1.id from user t1
left join (select id ,date from user where id=1) t2
on t1.id=t2.id;
But this query doesn't work like I thought.
Can anyone please tell me how above join query works ? And how can I improve it ? So that I got the required result.
For testing of above query use this queries =>
create table user(
name varchar(20),
date date,
id integer
);
insert into user values("A",'2021-01-21',1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("",null,1);
insert into user values("B",'2021-01-20',2);
select t1.name, t2.date ,t1.id from user t1
left join (select id ,date from user where id=1) t2
on t1.id=t2.id;
The first problem is that you are joining a table with itself on the condition t1.id = t2.id. So if you have 4 rows with id=1 and 3 rows with id=2 just as an example, you will end up with a result that had 4 * 4 + 3 * 3 = 25 rows. In your specific case you will end up with 6 * 6 + 1 * 1 = 37 rows.
The second problem is that you have hard-code selecting id=1 in your subquery:
(select id ,date from user where id=1) t2
This can't be the appropriate value for all possible rows.
You could try the obvious:
select
t1.name,
ifnull(t1.date, (select t2.date from user t2 where t2.date is not null and t2.id = t1.id limit 1)) as date,
t1.id
from user t1
;
see db-fiddle
name
id
date
A
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
1
2021-01-21
B
2
2021-01-20
But better would be to use a join:
select u.name, ifnull(u.date, sq.date) as date, u.id
from user u join (
select id, min(date) as date from user group by id
) sq on u.id = sq.id
;
see db-fiddle
I would expect the second version using a join to be more efficient because the first version has a dependent subquery that has to get executed for every row that has a null date.
You don't need a join. Just use a window function:
select name,
max(date) over (partition by id) as date,
id
from users;
Note that your sample data doesn't match the data in the question. That data suggests:
select max(name) over (partition by id) as name,
max(date) over (partition by id) as date,
id
from user;
Here is a db<>fiddle.

Joining two tables with date ranges

I have two SQL tables that contain start and end dates.
Table 1: Name, AddedDate
Table 2: Name, RemovedDate
I'm am looking to join these two tables and dump the data into a temp table to show when a name was added and removed from a list.
The same name may have been added and removed multiple times.
Desired Output Example
- Name, AddedDate, RemovedDate
- Jane, 2017-02-01, 2017-02-03
- Bill, 2017-01-28, (blank)
- Mike, 2017-01-15, 2017-01-19
- Jane, 2017-01-13, 2017-01-14
Can someone please help? Thanks.
Another option is an OUTER APPLY (If SQL Server)
Example
Declare #Table1 table (Name varchar(25),AddedDate date)
Insert Into #Table1 Values
('Jane', '2017-02-01'),
('Bill', '2017-01-28'),
('Mike', '2017-01-15'),
('Jane', '2017-01-13')
Declare #Table2 table (Name varchar(25),RemovedDate date)
Insert Into #Table2 Values
('Jane', '2017-02-03'),
('Mike', '2017-01-19'),
('Jane', '2017-01-14')
Select A.Name
,A.AddedDate
,B.RemovedDate
From #Table1 A
Outer Apply (
Select RemovedDate=min(RemovedDate)
From #Table2
Where Name=A.Name
and RemovedDate>=A.AddedDate
) B
Returns
Name AddedDate RemovedDate
Jane 2017-02-01 2017-02-03
Bill 2017-01-28 NULL
Mike 2017-01-15 2017-01-19
Jane 2017-01-13 2017-01-14
Using correlated subquery to consider only the last time the name was added (and potentially removed)...
select Name,
AddedDate,
(select max(RemovedDate)
from table_2
where Name=q1.Name
and RemovedDate >= q1.AddedDate) as RemovedDate
from (select Name,
max(AddedDate) as AddedDate
from table_1
group by name) as q1
order by AddedDate desc,
Name;
Same correlated subquery approach to show every time a name was added and removed...
select Name,
AddedDate,
(select min(RemovedDate)
from table_2
where Name=q1.Name
and RemovedDate >= t1.AddedDate) as RemovedDate
from table_1 t1
order by AddedDate desc,
Name;
You could use left join on name
select t1.name, t1.AddedDate, t2.RemovedDate
from table1 t1
left join table2 t2 on t1.name = t2.name
order by name, t1.AddedDate, t2.RemovedDate
For SQL Server Use the below script
;WITH CTE
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY T1.Name ORDER BY T1.AddedDate DESC,T2.RemovedDate DESC)
T1.Name,
T1.AddedDate,
T2.RemovedDate
FROM Table1 T1
LEFT JOIN Table2 T2
ON LTRIM(RTRIM(T1.name)) = LTRIM(RTRIM(T2.name))
)
SELECT
*
FROM CTE
WHERE SeqNo = 1
If you want all the records for a Name, such as if a name was added and removed multiple times and you want each of the dates, then just execute without the
WHERE SeqNo = 1
part

Mysql Join query with count table records with same date

Hello i am having two different table with same field created_date (datetime)
now i want records which counts daywise records with joining table i have done for individual counting as below query :
SELECT DATE(created_date), COUNT(*) FROM table1 GROUP BY DAY(created_date)
SELECT DATE(created_date), COUNT(*) FROM table2 GROUP BY DAY(created_date)
and i am getting results for individuals something like this:
RESULT I NEED :
DATE(created_date) count(table1) count(table2)
2016-12-01 10 3
2016-12-02 1 0
2016-12-05 1 0
2016-11-29 1 0
2016-11-30 4 1
Now i just want to join these result WITH INDIVIDUAL VIEW COUNT ACCORDING TO TABLE can anyone please help me out with this profile....
First take a UNION between your two tables, then use conditional aggregation to determine the counts for each of the two tables. Note that I introduce a field called table_name to keep track of data from each of the two tables.
SELECT t.created_date,
SUM(CASE WHEN t.table_name = 'one' THEN 1 ELSE 0 END) AS count_table_one,
SUM(CASE WHEN t.table_name = 'two' THEN 1 ELSE 0 END) AS count_table_two
FROM
(
SELECT DATE(created_date) AS created_date, 'one' AS table_name
FROM table1
UNION ALL
SELECT DATE(created_date), 'two'
FROM table2
) t
GROUP BY t.created_date
I used DATE consistently everywhere to make the query correct.
Try This:
SELECT created_date, sum(countTable1) countTable1,
sum(countTable2) countTable2
FROM (
SELECT DATE(created_date) created_date, COUNT(*) countTable1, NULL countTable2
FROM table1 GROUP BY DAY(created_date)
UNION ALL
SELECT DATE(created_date) created_date, NULL, COUNT(*) countTable2
FROM table2 GROUP BY DAY(created_date)) t GROUP BY t.created_date
You have a problem in your queries, you are grouping by DAY(date) and showing 'date' so the result will be first date with day(date), yet repeating it to avoid misunderstanding :)
select IFNULL(A.cd, B.cd), A.cnt, B.cnt from
(SELECT DAY(created_date) d, DATE(created_date) cd, COUNT(*) cnt
FROM table1 GROUP BY DAY(created_date)) as A
LEFT JOIN
(SELECT DAY(created_date) d, DATE(created_date) cd , COUNT(*) cnt
FROM table2 GROUP BY DAY(created_date)) B ON B.d = A.d
Its not too hard just use union if no need to allow duplicate row else use union all for all(means allow duplicate as well).
SELECT DATE(created_date), COUNT(*) FROM table1 GROUP BY DAY(created_date)
UNION
SELECT DATE(created_date), COUNT(*) FROM table2 GROUP BY DAY(created_date)
SELECT T.create_date,ISNULL(T.count,0)AS Counttable1,ISNULL(X.count,0)AS Counttable2 FROM(SELECT DATE(created_date) AS create_date,COUNT(*) as count FROM table1 GROUP BY DAY(created_date)) AS T LEFTJOIN(SELECT DATE(created_date) AS create_date, COUNT(*) as count FROM table2 GROUP BY DAY(created_date))AS X ON T.create_date=X.create_date
You actually need a SQL UNION. JOIN natuarually eliminate counts becuase the maytch fields. I.e. if you had 2016-12-01 in both table1 andtable2 then a JOIN on created_date would give you a count of 1 instead of a count of 2.
SELECT DATE(total.created_date), COUNT(*)
FROM (
SELECT created_date FROM table1
UNION ALL
SELECT created_date FROM table2) as total
GROUP BY total.created_date
HERE you simply union the two tables since they have a matching column name. Then you get back every date from both tables. That is in the inner query. The outer query then does the counting.
Hope that makes sense.

SQL Find date range gaps in Table

Good day.
I seem to be struggling with what seems like a simple problem.
I have a table that has a value connected to a date (Monthly) for a finite number of ID's
ie. Table1
ID | Date ---| Value
01 | 2015-01 | val1
01 | 2015-02 | val2
02 | 2015-01 | val1
02 | 2015-03 | val2
So ID: 02 does not have a value for date 2015-02.
I would like to return all ID's and Dates that do not have a value.
Date range is: select distinct date from Table1
I can't seem to think outside the realms of selecting and joining on the same table.
I need to include the ID in my select to I can somehow select the ID and Date range that exists for that ID and compare to the entire date range, to get all the dates for each ID that isn't in the "entire" date range.
Please advise.
Thank you
Not very clear about your last two sentences. But you can play with the following query with different #max_days and #min_date:
-- DROP TABLE table1;
CREATE TABLE table1(ID int not null, `date` date not null, value varchar(64) not null);
INSERT table1(ID,`date`,value)
VALUES (1,'2015-01-01','v1'),(1,'2015-01-02','v2'),(2,'2015-01-01','v1'),(2,'2015-01-03','v2'),(4,'2015-01-01','v1'),(4,'2015-01-04','v2');
SELECT * FROM table1;
SET #day=0;
SET #max_days=5;
SET #min_date='2015-01-01';
SELECT i.ID,d.`date`
FROM (SELECT DISTINCT ID FROM table1) i
CROSS JOIN (
SELECT TIMESTAMPADD(DAY,#day,#min_date) AS `date`,#day:=#day+1 AS day_num
FROM table1 WHERE #day<#max_days) d
LEFT JOIN table1 t
ON t.ID=i.ID
AND t.`date`=d.`date`
WHERE t.`date` IS NULL
ORDER BY i.ID,d.`date`;
I now understand your requirement of dates being taken from the table; you want to find any gaps in the date ranges for each id.
This does what you need, but can probably be improved. Explanation below and you can view a working example.
DROP TABLE IF EXISTS Table1;
DROP TABLE IF EXISTS Year_Month_Calendar;
CREATE TABLE Table1 (
id INTEGER
,date CHAR(7)
,value CHAR(4)
);
INSERT INTO Table1
VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
SELECT ID_Year_Month.id, ID_Year_Month.date, Table1.id, Table1.date
FROM (
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
) AS ID_Year_Month
LEFT JOIN Table1
ON ID_Year_Month.id = Table1.id AND ID_Year_Month.date = Table1.date
-- WHERE Table1.id IS NULL
ORDER BY ID_Year_Month.id, ID_Year_Month.date
Explanation
You need a calendar table which contains all dates (year/months) to cover the data you are querying.
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
The inner select creates a table with all dates between the min and max date for each id.
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
This is then LEFT JOINED to the original table to find the missing rows.
If you only want to return the missing row (my query displays the whole table to show how it works), add a WHERE clause to restrict the output to those rows where an id and date is not returned from Table1
Original answer before comments
You can do this without a tally table, since you say
Date range is: select distinct date from Table1
I've slightly changed the field names to avoid reserved words in SQL.
SELECT id_table.ID, date_table.`year_month`, table1.val
FROM (SELECT DISTINCT ID FROM table1) AS id_table
CROSS JOIN
(SELECT DISTINCT `year_month` FROM table1) AS date_table
LEFT JOIN table1
ON table1.ID=id_table.ID AND table1.`year_month` = date_table.`year_month`
ORDER BY id_table.ID
I've not filtered the results, in order to show how the query is working. To return the rows where only where a date is missing, add WHERE table1.year_month IS NULL to the outer query.
SQL Fiddle
You will need a tally table(s) or month/year tables. So you can then generate all of the potential combinations you want to test with. As far as exactly how to use it your example could use some expanding on such as last 12 months, last3 months, etc. but here is an example that might help you understand what you are looking for:
http://rextester.com/ZDQS5259
CREATE TABLE IF NOT EXISTS Tbl (
ID INTEGER
,Date VARCHAR(10)
,Value VARCHAR(10)
);
INSERT INTO Tbl VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
SELECT yr.YearNumber, mn.MonthNumber, i.Id
FROM
(
SELECT 2016 as YearNumber
UNION SELECT 2015
) yr
CROSS JOIN (
SELECT 1 MonthNumber
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
) mn
CROSS JOIN (
SELECT DISTINCT ID
FROM
Tbl
) i
LEFT JOIN Tbl t
ON yr.YearNumber = CAST(LEFT(t.Date,4) as UNSIGNED)
AND mn.MonthNumber = CAST(RIGHT(t.Date,2) AS UNSIGNED)
AND i.ID = t.ID
WHERE
t.ID IS NULL
The basic idea to determine what you don't know is to generate all possible combinations of something could be. E.g. Year X Month X DISTINCT Id and then join back to figure out what is missing.
Probably not the prettiest but this should work.
select distinct c.ID, c.Date, d.Value
from (select a.ID, b.Date
from (select distinct ID from Table1) as a, (select distinct Date from Table1) as b) as c
left outer join Table1 d on (c.ID = d.ID and c.Date = d.Date)
where d.Value is NULL

Mysql union select error

I am trying to display all records from table1 even if the catid not existing in table2 (all employee in table2 should have all catid from table1 with 0 days if not exising in table2) with the following sql query but getting an error
Error Code: 1054. Unknown column 'catid' in 'group statement'
select empid,days from table2 union select catid from
table1 group by empid, catid;
table1:
catid
1
2
3
table2:
empid catid days (computed column count(*))
1000 1 8
1000 3 10
expected result:
empid catid days
1000 1 8
1000 2 0 <---catid 2 and days 0 if catid not existing in table2 for empid 1000
1000 3 10
That is not the function of the union statement. Union statement does a set like capability which merging two sets. What you are looking for a is a join with the table 1 where you do a count and group by catid. Your data model to achieve this output itself is grievously wrong ;)
select employeeid, catid, sum(days) from table1, table2 group by employeeid, catid;
You just need a LEFT JOIN:
Select tab2.empid, tab2.catid, ifnull(tab2.days, 0)
from tab2
left join tab1 on tab2.catid = tab1.catid
Please note : While doing a UNION the number and type of the columns present in the first select should be the same as the next Selects.
So you need to first make the select columns in sync first.
can you check this and add empid similarly.
SELECT TABLE1.CATID, IFNULL(TABLE2.DAYS,0) FROM table1 LEFT OUTER JOIN
table2 ON table1.catid = table2.catid
Please use LEFT JOIN with IFNULL.
Select table2.empid, table1.catid, IFNULL(table2.days, 0) from table2
LEFT JOIN table1 ON table2.catid = table1.catid;