MySQL query to get SUM and merged results from 3 tables - php

I am having 3 tables in my MySQL DB. The Tables and columns are like in sample below
Table User
id, name, dob, gross_salary
----------------------------------
1, test1, 12 Mar 90, 50000
2, test2, 10 Jan 85, 45000
Table Wage
ida, date, allowence_paid
------------------------------
1, 10 Jul 13, 12000
2, 10 Aug 13, 23000
2, 12 Aug 13, 1000
1, 15 Aug 13, 15000
Table Loan
id, date, loan_amount
--------------------------
2, 05 Jul 13, 500
1, 05 Aug 13, 2500
1, 06 Aug 13, 1200
I need these three tables merged in results for Aug 13 like
id, name, allowence_paid, loan_Amount, paid
--------------------------------------------
1, test1, 15000, 3700, 11300
2, test2, 24000, 0, 24000
SUM of two columns from two different tables joined to another table is required in my case.
Can I get help for the query? I have experimented as per MySQL JOIN with SUM and 3 tables and failed.

This seems to work:
select *, allowence_paid-loan_amount as paid
from
(
select User.id as UserId, User.name as UserName, sum(allowence_paid) as allowence_paid
from Wage join User on Wage.ida=User.id
and Wage.date between '2013-08-01 00:00:00' and '2013-08-31 23:59:00'
group by UserId
) W JOIN
(
select User.id as UserId, User.name as UserName, sum(coalesce(loan_amount,0)) AS loan_amount
from Loan right outer join User on Loan.id=User.id
and Loan.date between '2013-08-01 00:00:00' and '2013-08-31 23:59:00'
group by UserId
) L ON W.UserId = L.UserId
SQL fiddle here.

Related

Decrement cells when records are deleted

So I have a simple MySQL table (block) as such:
SELECT * FROM `block` WHERE 1 ORDER BY `year`, `month`, `day`, `block`;
id year month day block te status
20000 2015 12 28 1 100000 1
20001 2015 12 28 2 100000 1
20002 2015 12 28 3 100001 1
20003 2015 12 28 4 100001 1
20004 2015 12 29 1 100001 1
20005 2015 12 29 2 100001 1
20006 2015 12 29 3 100002 1
20066 2015 12 30 1 100003 1
20078 2015 12 30 1 100007 1
20070 2015 12 30 1 100004 1
20067 2015 12 30 2 100003 1
20071 2015 12 30 2 100004 1
20079 2015 12 30 2 100007 1
20072 2015 12 30 3 100004 1
20080 2015 12 30 3 100007 1
20068 2015 12 30 3 100003 1
20069 2015 12 30 4 100003 1
20073 2015 12 30 4 100004 1
20074 2015 12 31 1 100004 1
20075 2015 12 31 1 100000 1
20076 2015 12 31 2 100000 1
20077 2015 12 31 3 100000 1
20007 2016 1 1 1 100017 1
20008 2016 1 1 2 100017 1
20009 2016 1 1 3 100017 1
My question is, how can I shift all the remaining rows up if I delete a contiguous section of rows?
For example, if I delete all blocks for 2015-12-28, I want all the remaining blocks to decrement to occupy the unused blocks on the schedule.
Let me know if this is confusing or unclear. Thanks.
I think a bit more background is required. What is your eventual goal?
Two options...
First Option
Check out the link below on numbering rows in MySQL. You can use something like this to make your blocks dynamic.
http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/
Second Option
If you are pulling data by day, just pull the data in order of the id and you can just assume that the blocks are in order in whatever application you use the data in.
For example, if I delete all blocks for 2015-12-28, I want all the remaining blocks to decrement to occupy the unused blocks on the schedule.
You do not actually specify what decrementing means. I think you want to shift the dates in all the blocks. But look here:
20000 2015 12 28 1 100000 1
20001 2015 12 28 2 100000 1
20002 2015 12 28 3 100001 1
20003 2015 12 28 4 100001 1
20004 2015 12 29 1 100001 1
20005 2015 12 29 2 100001 1
20006 2015 12 29 3 100002 1
20066 2015 12 30 1 100003 1
We have four blocks for 2015-12-28 and three for 2015-12-29. Say we delete the four 28's. What happens? Do three of the 29's shift to 28's, and the 30 become 29? Or do the three 29's and the 30 shift to fill the four places left by the 28's?
Depending on the strategy you need to choose different strategies. Perhaps you should define what exactly a block is, and what data is outside a "block", and supply a couple examples of a shift (with different numbers of days - e.g., 28 28 29 30 and you delete the two 28. BTW, can you delete only one? If so, what would happen then?).
Say each date can hold a fixed number of records
For example you can first count how many rows you're going to delete
SELECT COUNT(*) FROM ... WHERE day=28 AND month=12 AND year=2015
and then select the next items that have a later date. You then shift them, but you need to do this iteratively (once the 29's are gone, the 30's will shift, and so on). This may become quite expensive to calculate.
But in this case you might separate the date from the data:
TableA
2015 12 28
2015 12 28
2015 12 28
TableB
20000 100000 1
20001 100000 1
Now you need to join the tables in such a way that you can... well, join them. Whatever their cardinality. You might have a longer TableA than TableB, or vice versa.
This would be pretty easy in some RDBMS. In MySQL it's pretty awful (you can't even use CREATE VIEW to simplify the syntax because you'd need either a variable or a subquery in the view), and for small data sets you'd be better off by selecting TableA, then selecting TableB, and using a cycle in PHP:
// Run the query from tableA to get the dates
$TableADataSet = array();
while ($tuple = SQLFetch($rs)) {
$TableADataSet = $tuple;
}
// Run the query from tableB to get the rest
$TableBDataSet = array();
while ($tuple = SQLFetch($rs)) {
$TableBDataSet = $tuple;
}
// Now put them together.
for (;;) {
if (empty($TableADataSet)) {
if (empty($TableBDataSet)) {
break;
}
$TupleA = $EmptyRowFormattedAsTableA;
$TupleB = array_pop($TableBDataSet);
} else {
$TupleA = array_pop($TableADataSet);
if (empty($TableBDataSet)) {
$TupleB = $EmptyRowFormattedAsTableB;
} else {
$TupleB = array_pop($TableBDataSet);
}
}
// Now you have a row with dates and data "aligned".
// Missing data are taken by the sample rows EmptyRowFormattedAs... .
}
The result could be (simplifying)
2015 12 28 RowA
2015 12 28 RowB
2015 12 29 RowC
NULL NULL NULL RowD
and if you delete RowB, the rightmost part of the rows shifts upwards:
2015 12 28 RowA
2015 12 28 RowC
2015 12 29 RowD
In MySQL you first need to number the rows, and you have no RECNO() or ROW_NUMBER() function, so you do it like
SELECT #a:=#a+1 AS ranka,data1.* FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data1, (SELECT #a:=0) AS init1;
and get your dates with a row number.
1 2015 12 28
2 2015 12 28
3 2015 12 29
You do the same for TableB. But now you have to do the "horizontal join" trick. We can be in one of these three cases:
B is longer A is longer Same length
A B A B A B
A B A B A B
- B A - A B
The "A B" part we can get with a JOIN. The "A -" part we get with a LEFT JOIN with a WHERE TableB.primarykey IS NULL. The "- B" part we get with a RIGHT JOIN WHERE TableA.primarykey IS NULL. Finally we put it all together with a UNION. And these three queries will need to be made with the awkward syntax above.
The (quite convoluted) query coming out of all this is
SELECT * FROM
( SELECT #a:=#a+1 AS rank1a, data1.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data1,
(SELECT #a:=0) AS init1
) AS ta
JOIN
( SELECT #b:=#b+1 AS rank1b, data2.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data2,
(SELECT #b:=0) AS init2 )
) AS tb
ON (rank1a = rank1b)
UNION
SELECT * FROM
( SELECT #c:=#c+1 AS rank2a, data3.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data3,
(SELECT #c:=0) AS init3
) AS tc
LEFT JOIN
( SELECT #d:=#d+1 AS rank2b, data4.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data4,
(SELECT #d:=0) AS init4 )
) AS td
ON (rank2a = rank2b) WHERE ( rank2b IS NULL )
UNION
SELECT * FROM
( SELECT #e:=#e+1 AS rank3a, data5.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data5,
(SELECT #e:=0) AS init5
) AS te
LEFT JOIN
( SELECT #f:=#f+1 AS rank3b, data6.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data6,
(SELECT #f:=0) AS init6 )
) AS tf
ON (rank3a = rank3b) WHERE ( rank3a IS NULL )
which, from a performance point of view, is probably a hog.

MySQL Multiplied query result (LEFT JOIN, SUM, COUNT, GROUP BY)

Trying to get a simple SUM and COUNT from a table that takes a couple of joins and some math. I can't get it right.
I have 4 tables:
USERS
user_id, name
1, user1
2, user2
3, user3
4, user4
RESULTS
user_id, plus, minus
1, 1, 0
1, 1, 0
1, 1, 0
3, 0, 1
3, 1, 0
3, 1, 0
3, 1, 0
NOTES
user_id, note
1, lorem ipsum
3, abc
1, qwerty
3, qwerty
MESSAGES
user_id, message
1, lorem ipsum
3, abc
1, qwerty
3, qwerty
3, qwerty
3, qwerty
3, qwerty
I would like to sum all the result of subtracting plus from the minus, amount of notes and amount of messages of users.
My query:
SELECT u.`user_id`, SUM(w.`plus`-w.`minus`), COUNT(g.`user_id`), COUNT(m.`user_id`)
FROM `users` u
LEFT JOIN `results` w ON u.`user_id` = w.`user_id`
LEFT JOIN `notes` g ON u.`user_id` = g.`user_id`
LEFT JOIN `messages` m ON u.`user_id` = m.`user_id`
GROUP BY 1
Expected result should looks like:
user_id: sum(plus-minus) amount of notes, amount of messages
1: 3, 2, 2
3: 2, 2, 5
unfortunatelly query returns:
user_id: sum(plus-minus) amount of notes, amount of messages
1: 12, 12, 12
3: 20, 40, 40
Any help is greatly appreciated.
You should manually count the records in a subquery for every user_id so you will get correct value.
SELECT u.user_ID, u.Name,
COALESCE(r.totalResult, 0) totalResult,
COALESCE(n.totalNotes, 0) totalNotes,
COALESCE(m.totalMessages, 0) totalMessages
FROM users u
LEFT JOIN
(
SELECT user_ID, SUM(plus - minus) totalResult
FROM Results
GROUP BY user_ID
) r ON u.user_ID = r.user_ID
LEFT JOIN
(
SELECT user_ID, COUNT(*) totalNotes
FROM Notes
GROUP BY user_ID
) n ON u.user_ID = n.user_ID
LEFT JOIN
(
SELECT user_ID, COUNT(*) totalMessages
FROM Messages
GROUP BY user_ID
) m ON u.user_ID = m.user_ID
But if you have set an auto_incremented column in every table, the subquery isn't needed since you can distinctly count from it.

SQL statement with PHP - Select the latest added row from each vendor

This question is probably easy to solve, but here it comes.
I would like to get the latest data from each vendor (the latest row inserted into the database for each vendor).
This is some sample data from MySQL:
id vendorId vendor url delay price0 price1 price2 price3 price4 price5 price6 date
1 1 vendor1 vendor1.com 0 12 15 18 14 25 28 25 2012-01-18 09:43:40
2 2 vendor2 vendor2.com 0 12 15 18 14 25 28 25 2012-01-18 09:43:40
3 1 vendor1 vendor1.com 0 15 17 122 12 30 52 53 2012-01-18 10:02:40
4 2 vendor2 vendor2.com 0 13 12 123 16 54 61 91 2012-01-18 10:02:40
I would like the output to be:
id vendorId vendor url delay price0 price1 price2 price3 price4 price5 price6 date
3 1 vendor1 vendor1.com 0 15 17 122 12 30 52 53 2012-01-18 10:02:40
4 2 vendor2 vendor2.com 0 13 12 123 16 54 61 91 2012-01-18 10:02:40
What would the SQL be and how would I put the values into this array?
$vendor = array(
array('vendorId' => '1', 'vendor' => 'vendor1', 'url' => 'vendor1', 'delay' => 0, 'price0' => 12, 'price1' => 15, 'price2' => 26, 'price3' => 14, 'price4' => 25, 'price5' => 64, 'price6' => 512),
array('vendorId' => '2', 'vendor' => 'vendor2', 'url' => 'vendor2', 'delay' => 0, 'price0' => 12, 'price1' => 15, 'price2' => 26, 'price3' => 14, 'price4' => 25, 'price5' => 64, 'price6' => 512)
);
Thanks in advance
Also, I forgot to mention: There will be more than just 2 vendors in the database, so I'd prefer that they'd be picked up per auto.
SELECT max( id ) AS max_id, vendorId, url, delay,
price0, price1, price2, price3, price4, price5, price6, date
FROM `vendors`
GROUP BY vendorId
You updated your question with how you should obtain the resultset into an array.
Refer here: http://php.net/manual/en/function.mysql-fetch-array.php
$row = mysql_fetch_array($result);
You will then be able to add each row as an associative array to another array containing all your vendors, accessible by key/value.
Try this query -
SELECT v1.* FROM vendors v1
JOIN (
SELECT vendorId, MAX(date) date FROM vendors GROUP BY vendorId
) v2
ON v1.vendorId = v2.vendorId AND v1.date = v2.date
Not sure if the syntax is correct, but this is roughly how I would do it.
SELECT
`id`, `vendorId`, `vendor`, `url`, `delay`, `price0`,
`price1`, `price2`, `price3`, `price4`, `price5`,
`price6`, `date`
FROM
`vendor_table_name`
GROUP BY
`vendor_id`
HAVING
`date` = max(`date`)
Try below :
select * from vendor_table_name as ts
left join
(select max(id) as id from vendor_table_name group by vendorId) as tsm
on ts.id=tsm.id
However it has limit after that it will be slow approx after 50K - 80K rows
Best way is to make a view for latest records and make join with view.
like below :
create view latestrecords as
select max(id) as id,vendorId from vendor_table_name group by vendorId
and do join with view :
select * from vendor_table_name as ts
left join
latestrecords as tsm
on ts.id=tsm.id
Try this
SELECT * FROM vendor_table_name ORDER BY date DESC
For the array
$result = (query result)
foreach($result as $r){
$array = array("name" => $r->name, .....);
}

CodeIgniter SQL query - how to sum values for each month?

I have the following table:
//table_1
record_id user_id plant_id date cost
1 1 1 2011-03-01 10
2 1 1 2011-03-02 10
3 1 1 2011-04-10 5
4 1 2 2011-04-15 5
I would like to build a query (if possible using CI Active Records, but MySQL is fine) in which I generate the following result:
[1] => [1] => [March 2011] [20]
=> [April 2011] [5]
[2] => [March 2011] [0]
=> [April 2011] [5]
I have tried using $this->db->group_by but I think I'm not using it correctly.
If anyone could give me a pointer or roadmap to get this done it would be much appreciated -- thanks!
Sample table
drop table if exists t;
create table t( record_id int, user_id int, plant_id int, date datetime, cost float);
insert t select
1 ,1, 1 ,'2011-03-01', 10 union all select
2 ,1, 1 ,'2011-03-02', 10 union all select
3 ,1, 1 ,'2011-04-10', 5 union all select
4 ,1, 2 ,'2011-04-15', 5;
Because you want to see the row with 0, you need to do a cross join between the year-month and all user-plants.
select up.user_id, up.plant_id, ym2, ifnull(sum(t.cost),0) totalcost
from (select distinct date_format(date, '%Y-%m') ym, date_format(date, '%M %Y') ym2 from t) dates
cross join (select distinct t.user_id, t.plant_id from t) up
left join t on date_format(t.date, '%Y-%m') = dates.ym
and up.user_id=t.user_id
and up.plant_id=t.plant_id
group by up.user_id, up.plant_id, ym2, ym
order by up.user_id, up.plant_id, date(concat(ym,'-1'));
The fact that Month Year does not sort correctly also requires the complex treatment of the dates for ordering purposes at the end.
This is a pretty intense way to do it and it would be far preferable to store the month-year as a column itself if you need to make these queries frequently, but this is basically how it works:
SELECT CONCAT(MONTHNAME(date), ' ', YEAR(date)) AS monthyear, COUNT(*) AS count GROUP BY YEAR(date), MONTH(date), plant_id;
That should get you the resultset you're looking for.

Seasonal Pricing for an Entity.

I am trying to manage seasonal prices for hotel rooms.
The only way that I can think of doing it would be to use:
A = Room Rate
B = Service Charge for room
Imagine that the table has a roomId column which is omited from below.
| DayDate |EndDate | A | B
-----------------------------------------------
| 2010/07/1 |2010/07/2 | 200 | 40
| 2010/07/3 |2010/07/4 | 150 | 40
| 2010/07/5 |2010/07/5 | 150 | 50
| 2010/07/6 |2010/07/7 | 200 | 50
| 2010/07/8 |2010/07/9 | 100 | 60
etc.. (table taken from another question).
The problem is: I don't want my seasons to be year specific.
Seasons for rooms shouldn't change year on year. I don't want my users to have to enter the seasonal information several times.
I am also going to have thousands of rooms, so I don't know a way to make this easily manageable.
I'm using mysql and php.
Start with a season table that defines the date ranges for the seasons. It should have a primary key field, say season_id. Then have another table to store room, price and season_id. The season_id is a foreign key to the season table.
Create Table Prices
(
MonthStart int not null
, DayStart int not null
, MonthEnd int not null
, DayEnd int not null
, A int not null
, B int not null
)
Insert Prices( MonthStart, DayStart, MonthEnd, DayEnd, A, B )
Select 7, 1, 7, 2, 200, 40
Union All Select 7, 3, 7, 4, 150, 40
Union All Select 7, 5, 7, 5, 150, 50
Union All Select 7, 6, 7, 7, 200, 50
Union All Select 7, 8, 7, 9, 100, 60
It should be noted that this approach presumes that the boundaries of the seasons are specific to the month and day regardless of year or circumstance. In addition, you'll have to decide how to handle leap year. Another approach which might be simpler is to simply enumerate every day of the year:
Create Table Prices
(
MonthStart int not null
, DayStart int not null
, A int not null
, B int not null
, Constraint PK_Prices Primary Key ( MonthStart, DayStart )
)
Insert Prices( MonthStart, DayStart, A, B )
Select 7, 1, 200, 40
Union All Select 7, 2, 200, 40
Union All Select 7, 3, 150, 40
Union All Select 7, 4, 150, 40
Union All Select 7, 5, 150, 50
Union All Select 7, 6, 200, 50
Union All Select 7, 7, 200, 50
Union All Select 7, 8, 100, 60
Union All Select 7, 9, 100, 60

Categories