SELECT and SUM from multiple tables - mysql

I need single SQL query for SELECT list of jobs including SUM() of sme specific detail type.
I have database with transport data. Tables looks like that:
job:
idjob | customer
1 | 45
2 | 38
3 | 15
job-detail:
iddet | idjob | type | value
1 | 1 | range | 100
2 | 1 | range | 85
3 | 1 | range | 12
4 | 1 | price | 64
4 | 1 | price | 5
5 | 1 | note | Some text here
6 | 2 | range | 150
7 | 2 | price | 32
8 | 2 | note | Some text here
9 | 2 | range | 35
I need this output:
idjob | customer | total_range | total_price
1 | 45 | 197 | 69
2 | 38 | 185 | 32
3 | 15 | 0 | 0

you can use left join with conditional aggregation
select a.idjob,customer,
sum(case when type='range' then value end) as total_range,
sum(case when type='price' then value end) as total_price
from job a
left join job-detail b on a.idjob=b.idjob
group by a.idjob,customer

SELECT DISTINCT J.idjob, J.customer,
(SELECT SUM(CONVERT(INT, [VALUE])) FROM [dbo].[job-detail] WHERE TYPE = 'range' AND J.idjob = idjob) AS total_range,
(SELECT SUM(CONVERT(INT, [VALUE])) FROM [dbo].[job-detail] WHERE TYPE = 'price' AND J.idjob = idjob) AS total_price
FROM [dbo].[job] j
LEFT JOIN [dbo].[job-detail] JD ON J.idjob = JD.idjob

Related

How to subtract a value from a sum of total in MySql LEFT JOIN Query

I have 2 tables.
SELECT * FROM purchases;
+------+---------+-----------+
|purid | total_q | dstatus |
+------+---------+-----------+
| 1 | 45 | DELIVERED |
| 2 | 50 | LOADING |
| 3 | 24 | DELIVERED |
| 4 | 15 | DELIVERED |
| 5 | 10 | DELIVERED |
+------+---------------------+
SELECT * FROM warehouse;
+------+-------+---------+
| wid | purid | total_q |
+------+-------+---------+
| 4 | 1 | 45 |
| 5 | 4 | 15 |
| 9 | 3 | 10 |
| 12 | 3 | 5 |
+------+-------+---------+
I want to get "delivered" purchases with its amounts which are not already included in warehouse table. Here is the demo where I stuck: DEMO
The query which I use is:
SELECT p.purid as purid, (p.total_q - IFNULL(w.total_q,0)) as ntq
FROM `purchases` as p
LEFT JOIN `warehouse` as w ON p.purid=w.purid
WHERE p.dstatus = "DELIVERED" AND (p.total_q - IFNULL(w.total_q,0)) > 0
My desired output:
+-------+------+
| purid | ntq |
+-------+------+
| 5 | 10 |
| 3 | 9 |
+------+-------+
The problem is I could not subtract "total_q (24) from purchases table" from "sum total_q(10+5) from warehouse table".
You can try to use subquery aggregate warehouse by purid before join otherwise you might get multiple rows.
Query #1
SELECT p.purid as purid,
p.total_q - IFNULL(w.total_q,0) as ntq
FROM `purchases` as p
LEFT JOIN (
SELECT purid,SUM(total_q) total_q
FROM warehouse
GROUP BY purid
) as w ON p.purid=w.purid
WHERE p.dstatus = "DELIVERED"
AND p.total_q - IFNULL(w.total_q,0) > 0;
purid
ntq
3
9
5
10
View on DB Fiddle

How to sum values of two tables and group by date

I am building a trading system where users need to know their running account balance by date for a specific user (uid) including how much they made from trading (results table) and how much they deposited or withdrew from their accounts (adjustments table).
Here is the sqlfiddle and tables: http://sqlfiddle.com/#!9/6bc9e4/1
Adjustments table:
+-------+-----+-----+--------+------------+
| adjid | aid | uid | amount | date |
+-------+-----+-----+--------+------------+
| 1 | 1 | 1 | 20 | 2019-08-18 |
| 2 | 1 | 1 | 50 | 2019-08-21 |
| 3 | 1 | 1 | 40 | 2019-08-21 |
| 4 | 1 | 1 | 10 | 2019-08-19 |
+-------+-----+-----+--------+------------+
Results table:
+-----+-----+-----+--------+-------+------------+
| tid | uid | aid | amount | taxes | date |
+-----+-----+-----+--------+-------+------------+
| 1 | 1 | 1 | 100 | 3 | 2019-08-19 |
| 2 | 1 | 1 | -50 | 1 | 2019-08-20 |
| 3 | 1 | 1 | 100 | 2 | 2019-08-21 |
| 4 | 1 | 1 | 100 | 2 | 2019-08-21 |
+-----+-----+-----+--------+-------+------------+
How do I get the below results for uid (1)
+--------------+------------+------------------+----------------+------------+
| ResultsTotal | TaxesTotal | AdjustmentsTotal | RunningBalance | Date |
+--------------+------------+------------------+----------------+------------+
| - | - | 20 | 20 | 2019-08-18 |
| 100 | 3 | 10 | 133 | 2019-08-19 |
| -50 | 1 | - | 84 | 2019-08-20 |
| 200 | 4 | 90 | 378 | 2019-08-21 |
+--------------+------------+------------------+----------------+------------+
Where RunningBalance is the current account balance for the particular user (uid).
Based on #Gabriel's answer, I came up with something like, but it gives me empty balance and duplicate records
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal), #runningtotal:= #runningtotal+SUM(ResultsTotal)+SUM(TaxesTotal)+SUM(AdjustmentsTotal) as Balance, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, adjustments.amount AS AdjustmentsTotal, adjustments.date
FROM adjustments LEFT JOIN results ON (results.uid=adjustments.uid) WHERE adjustments.uid='1'
UNION ALL
SELECT results.amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, results.date
FROM results LEFT JOIN adjustments ON (results.uid=adjustments.uid) WHERE results.uid='1'
) unionTable
GROUP BY DATE ORDER BY date
For what you are asking you would want to union then group the results from both tables, this should give the results you want. However, I recommend calculating the running balance outside of MySQL since this adds some complexity to our query.
Weird things could start to happen, for example, if someone already defined the #runningBalance variable as part of the queries scope.
SELECT aggregateTable.*, #runningBalance := ifNULL(#runningBalance, 0) + TOTAL
FROM (
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal)
, SUM(ResultsTotal) + SUM(TaxesTotal) + SUM(AdjustmentsTotal) as TOTAL
, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, amount AS AdjustmentsTotal, date
FROM adjustments
UNION ALL
SELECT amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, date
FROM results
) unionTable
GROUP BY date
) aggregateTable

Selecting rows that are not defined

How to select rows which are not defined? Like row 2 have undefined day 3 and row 3 have undefined day 1. I want them to be 0 in result set.
+----+-----+-------+
| id | day | count |
+----+-----+-------+
| 1 | 1 | 262 |
| 1 | 2 | 685 |
| 1 | 3 | 984 |
| 2 | 1 | 692 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 3 | 3 | 741 |
+----+-----+-------+
EDIT:
I want select count from days 1, 2 and 3 (not whole table) and display 0 on undefined day.
We can get all unique id values in a Derived Table.
For day, you seem to want only 1,2 and 3 only. So we can directly consider these values only using UNION ALL.
CROSS JOIN between them to get all possible combinations.
LEFT JOIN from all_combinations table to the main table on id and day.
We can use Coalesce() function to consider 0 value for count, for the cases where there is no matching row in the main table
Try the following:
SELECT all_combinations.id,
all_combinations.day,
COALESCE(t.count, 0) AS count
FROM
(
SELECT ids.id, days.day
FROM
(SELECT DISTINCT id FROM your_table) AS ids
CROSS JOIN
(SELECT 1 AS day UNION ALL SELECT 2 UNION ALL SELECT 3) AS days
) AS all_combinations
LEFT JOIN your_table AS t
ON t.id = all_combinations.id AND
t.day = all_combinations.day
Result:
| id | day | count |
| --- | --- | ----- |
| 1 | 1 | 262 |
| 2 | 1 | 692 |
| 3 | 1 | 0 |
| 1 | 2 | 685 |
| 2 | 2 | 962 |
| 3 | 2 | 355 |
| 1 | 3 | 984 |
| 2 | 3 | 0 |
| 3 | 3 | 741 |
View on DB Fiddle

MySql: Summing the latest amounts banked for each category and type

I have four MySql tables (simplified here):
Table 1: factions (just a list to reference)
id | name
1 | FactionName1
2 | FactionName2
Table 2: currencies (just a list to reference)
id | name
1 | Currency1
2 | Currency2
3 | Currency3
Table 3: events (just a list to reference)
id | name | date
1 | Evebt1 | 2013-10-16
2 | Event2 | 2013-10-18 (Note: date out of order)
3 | Event3 | 2013-10-17
Table 4: event_banking (data entered after each event, remaining amount of each currency for each group)
id | faction_id | currency_id | event_id | amount
1 | 1 | 1 | 1 | 10
2 | 1 | 1 | 2 | 20
3 | 1 | 1 | 3 | 30
4 | 1 | 2 | 1 | 40
5 | 1 | 2 | 2 | 50
6 | 1 | 2 | 3 | 60
7 | 1 | 3 | 1 | 70
8 | 1 | 3 | 2 | 80
9 | 1 | 3 | 3 | 90
10 | 2 | 1 | 1 | 100
11 | 2 | 1 | 2 | 110
12 | 2 | 1 | 3 | 120
13 | 2 | 2 | 1 | 130
14 | 2 | 2 | 2 | 140
15 | 2 | 2 | 3 | 150
16 | 2 | 3 | 1 | 160
17 | 2 | 3 | 3 | 170
Note: Faction 2 didn't bank Currency 3 for Event 2
What I'm looking to be able to do is to get, for each currency, the total of the last banked (date wise) for each faction. (ie How much of each currency is currently banked in total if all factions are merged)
So, I need a table looking something like:
currency_id | total
1 | 130 (eg 20 + 110)
2 | 190 (eg 50 + 140)
3 | 250 (eg 80 + 170) <- Uses Event 3 for Group 2 as Event 2 doesn't exist
I can do basic joins etc, but I'm struggling to be able to filter the results so that I get the latest results for each Faction x Currency x Event so I can then sum them together to get the final total amounts for each currency.
I've tried various permutations of LEFT OUTER JOINs, GROUP BYss & HAVING COUNTs, and had some interesting (but incorrect results), and a variety of different error codes, but nothing remotely close to what I need.
Can anyone help?
I guess you can go on with something like this:
select eb.currency_id, sum(amount) as total
from events e
inner join (
select faction_id, currency_id, max(date) as md
from event_banking eb
inner join events e
on eb.event_id = e.id
group by faction_id, currency_id
) a
on e.date = a.md
inner join event_banking eb
on e.id = eb.event_id
and a.faction_id = eb.faction_id
and a.currency_id = eb.currency_id
group by currency_id;
Here is SQL Fiddle

Complex SQL query suggestions please

I have three tables with schema as below:
Table: Apps
| ID (bigint) | USERID (Bigint)| START_TIME (datetime) |
-------------------------------------------------------------
| 1 | 13 | 2013-05-03 04:42:55 |
| 2 | 13 | 2013-05-12 06:22:45 |
| 3 | 13 | 2013-06-12 08:44:24 |
| 4 | 13 | 2013-06-24 04:20:56 |
| 5 | 13 | 2013-06-26 08:20:26 |
| 6 | 13 | 2013-09-12 05:48:27 |
Table: Hosts
| ID (bigint) | APPID (Bigint)| DEVICE_ID (Bigint) |
-------------------------------------------------------------
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 1 |
| 4 | 3 | 3 |
| 5 | 1 | 4 |
| 6 | 2 | 3 |
Table: Usage
| ID (bigint) | APPID (Bigint)| HOSTID (Bigint) | Factor (varchar) |
-------------------------------------------------------------------------------------
| 1 | 1 | 1 | Low |
| 2 | 1 | 3 | High |
| 3 | 2 | 2 | Low |
| 4 | 3 | 4 | Medium |
| 5 | 1 | 5 | Low |
| 6 | 2 | 2 | Medium |
Now if put is userid, i want to get the count of rows of table rows for each month (of all app) for each "Factor" month wise for the last 6 months.
If a DEVICE_ID appears more than once in a month (based on START_TIME, based on joining Apps and Hosts), only the latest rows of Usage (based on combination of Apps, Hosts and Usage) be considered for calculating count.
Example output of the query for the above example should be: (for input user id=13)
| MONTH | USAGE_COUNT | FACTOR |
-------------------------------------------------------------
| 5 | 0 | High |
| 6 | 0 | High |
| 7 | 0 | High |
| 8 | 0 | High |
| 9 | 0 | High |
| 10 | 0 | High |
| 5 | 2 | Low |
| 6 | 0 | Low |
| 7 | 0 | Low |
| 8 | 0 | Low |
| 9 | 0 | Low |
| 10 | 0 | Low |
| 5 | 1 | Medium |
| 6 | 1 | Medium |
| 7 | 0 | Medium |
| 8 | 0 | Medium |
| 9 | 0 | Medium |
| 10 | 0 | Medium |
How is this calculated?
For Month May 2013 (05-2013), there are two Apps from table Apps
In table Hosts , these apps are associated with device_id's 1,1,1,4,3
For this month (05-2013) for device_id=1, the latest value of start_time is: 2013-05-12 06:22:45 (from tables hosts,apps), so in table Usage, look for combination of appid=2&hostid=2 for which there are two rows one with factor Low and other Medium,
For this month (05-2013) for device_id=4, by following same procedure we get one entry i.e 0 Low
Similarly all the values are calculated.
To get the last 6 months via query i'm trying to get it with the following:
SELECT MONTH(DATE_ADD(NOW(), INTERVAL aInt MONTH)) AS aMonth
FROM
(
SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5
)
Please check sqlfiddle: http://sqlfiddle.com/#!2/55fc2
Because the calculation you're doing involves the same join multiple times, I started by creating a view.
CREATE VIEW `app_host_usage`
AS
SELECT a.id "appid", h.id "hostid", u.id "usageid",
a.userid, a.start_time, h.device_id, u.factor
FROM apps a
LEFT OUTER JOIN hosts h ON h.appid = a.id
LEFT OUTER JOIN `usage` u ON u.appid = a.id AND u.hostid = h.id
WHERE a.start_time > DATE_ADD(NOW(), INTERVAL -7 MONTH)
The WHERE condition is there because I made the assumption that you don't want July 2005 and July 2006 to be grouped together in the same count.
With that view in place, the query becomes
SELECT months.Month, COUNT(DISTINCT device_id), factors.factor
FROM
(
-- Get the last six months
SELECT (MONTH(NOW()) + aInt + 11) % 12 + 1 "Month" FROM
(SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5) LastSix
) months
JOIN
(
-- Get all known factors
SELECT DISTINCT factor FROM `usage`
) factors
LEFT OUTER JOIN
(
-- Get factors for each device...
SELECT
MONTH(start_time) "Month",
device_id,
factor
FROM app_host_usage a
WHERE userid=13
AND start_time IN (
-- ...where the corresponding usage row is connected
-- to an app row with the highest start time of the
-- month for that device.
SELECT MAX(start_time)
FROM app_host_usage a2
WHERE a2.device_id = a.device_id
GROUP BY MONTH(start_time)
)
GROUP BY MONTH(start_time), device_id, factor
) usageids ON usageids.Month = months.Month
AND usageids.factor = factors.factor
GROUP BY factors.factor, months.Month
ORDER BY factors.factor, months.Month
which is insanely complicated, but I've tried to comment explaining what each part does. See this sqlfiddle: http://sqlfiddle.com/#!2/5c871/1/0