I'm trying to get some results from 2 tables, they are 1 to n relations. I want to get 2 columns from one table Extensao and one result from the second table HorarioExtensao. I'm trying to filter by month, but I just want to consider the lower date of the second column, since the same result can have multiple dates from different months.
I already tried to query on HorarioExtensao and then do another SELECT using the MIN(h.hora) BETWEEN $inicial and $final, but the problem is there I don't know where to put the INNER JOIN for Extensao to select e.nome and e.codigo. Thanks in advance.
$mes = $_GET["mes"];
$ano = $_GET["ano"];
$inicial = date("Y/m/d g:i:s",mktime(0,0,0,$mes,1,$ano));
$final = date("Y/m/t g:i:s",mktime(0,0,0,$mes,1,$ano));
$db = pg_connect("host=localhost dbname=saga user=**** password=****");
$result = pg_query($db,"SELECT e.nome, MIN(h.hora), e.codigo
FROM Extensao e
INNER JOIN HorarioExtensao h ON h.idExtensao = e.idExtensao
WHERE h.hora BETWEEN
to_timestamp('$inicial','YYYY/MM/DD HH:MI:SS') AND
to_timestamp('$final','YYYY/MM/DD HH:MI:SS')
GROUP BY 3,1");
EDIT
On tables:
- Extensao
----------------------
| ID | NOME | CODIGO |
|----|------|--------|
| 1 | N1 | 201 |
| 2 | N2 | 223 |
| 3 | N3 | 266 |
----------------------
- HorarioExtensao
---------------------------
| idExtensao | hora |
|------------|------------|
| 1 | 2012-01-21 |
| 1 | 2012-01-22 |
| 1 | 2012-02-15 |
| 1 | 2012-02-16 |
---------------------------
If I try to select month 2, I don't want to get any result, cause the lower date with the same idExtensao is for month 1. If I select month 1, I want to get 1 result only, which should be N1,2012-01-21,201. Also, I know 2012-01-21 is not a TIMESTAMP, I'm just simplifying.
The question leaves room for interpretation, to put it politely.
I think this might be what you are looking for: a LEFT [OUTER ] JOIN on a pre-aggregated table:
SELECT e.nome, h.min_hora, e.codigo
FROM extensao e
LEFT JOIN (
SELECT idextensao, min(hora) AS min_hora
FROM horarioextensao
GROUP BY 1
) h ON h.idextensao = e.id
AND h.min_hora >= to_timestamp($inicial, 'YYYY/MM/DD HH:MI:SS')
AND h.min_hora < to_timestamp($final, 'YYYY/MM/DD HH:MI:SS');
In subquery h, I pick the earliest hora per idextensao from horarioextensao before joining to extensao.
Note also how I pull up WHERE conditions into the LEFT JOIN condition. This way you get all rows from extensao and only those rows from horarioextensao that match the conditions.
Change to a plain JOIN if you only want rows from extensao that have a matching min_hora. Conditions can stay in the WHERE clause in this case.
The result for January in your example would be:
nome | min_hora | codigo
-----+------------+---------
N1 | 2012-01-21 | 201
N2 | null | 223
N3 | null | 266
And for February:
nome | min_hora | codigo
-----+------------+---------
N1 | null | 201
N2 | null | 223
N3 | null | 266
->sqlfiddle
Try this SQL Server syntax - you will need to translate it but it should give your the right idea
SELECT e.nome,MIN(h.hora),e.codigo
FROM Extensao e
CROSS APPLY
(SELECT TOP(1) h.hora
FROM HorarioExtensao h
WHERE h.idExtensao = e.idExtensao
AND
h.hora >= to_timestamp('$inicial','YYYY/MM/DD HH:MI:SS')
AND
h.hora < to_timestamp('$final','YYYY/MM/DD HH:MI:SS')
ORDER BY h.hora)
GROUP BY 3,1
Related
I am trying to get the sum of multiple rows from 2 different tables, but somehow the result returns multiple rows.
I need to get the SUM of quotation_item_amount (group by quotation_id) and invoice_item_amount (group by invoice_id) and if I query unpaid quotation, I need to get WHERE SUM(invoice) < SUM(quotation)
So here's my sample table
table client_project_id
+-------------------+-----------+----------------------+
| client_project_id | client_id | client_project_title |
+-------------------+-----------+----------------------+
| 23 | 5 | Project 1 |
| 17 | 9 | Project 2 |
| 54 | 7 | Project 3 |
+-------------------+-----------+----------------------+
table quotation
+--------------+-------------------+------------------+
| quotation_id | client_project_id | quotation_number |
+--------------+-------------------+------------------+
| 1 | 23 | Q/01/2020/001 |
| 2 | 17 | Q/01/2020/002 |
| 3 | 54 | Q/01/2020/003 |
+--------------+-------------------+------------------+
table quotation_item
+-------------------+--------------+-----------------------+
| quotation_item_id | quotation_id | quotation_item_amount |
+-------------------+--------------+-----------------------+
| 1 | 1 | 500 |
| 2 | 1 | 700 |
| 3 | 1 | 600 |
| 4 | 2 | 200 |
| 5 | 2 | 150 |
| 6 | 3 | 900 |
+-------------------+--------------+-----------------------+
table invoice
+--------------+-------------------+------------------+
| invoice_id | client_project_id | invoice_number |
+--------------+-------------------+------------------+
| 1 | 23 | I/01/2020/001 |
| 2 | 17 | I/01/2020/002 |
| 3 | 54 | I/01/2020/003 |
+--------------+-------------------+------------------+
table invoice_item
+-------------------+--------------+-----------------------+
| invoice_item_id | invoice_id | invoice_item_amount |
+-------------------+--------------+-----------------------+
| 1 | 1 | 500 |
| 2 | 1 | 700 |
| 3 | 1 | 600 |
| 4 | 2 | 200 |
| 5 | 2 | 150 |
| 6 | 3 | 900 |
+-------------------+--------------+-----------------------+
The result that I need to obtain is:
SUM of quotation_item_amount and SUM of invoice_item_amount PER client_project_id
To query WHERE SUM(invoice) < SUM(quotation)
Here is my latest try at the query
SELECT
SUM(quotation_item.quotation_item_amount) as quot_amt,
SUM(invoice_item.invoice_item_amount) as inv_amt,
data_client_project.client_project_id,
data_client.client_name
FROM data_client_project a
LEFT JOIN quotation b ON a.client_project_id = b.client_project_id
LEFT JOIN data_client d ON a.client_id = d.client_id
LEFT JOIN invoice i ON a.client_project_id = i.client_project_id
JOIN (
SELECT quotation_id,
SUM(c.quotation_item_amount) as quot_amt
FROM quotation_item c
GROUP BY c.quotation_id
) quotitem
ON b.quotation_id = quotitem.quotation_id
JOIN (
SELECT invoice_id,
SUM(e.invoice_item_price) as inv_amt
FROM invoice_item e
GROUP BY e.invoice_id
) invitem
ON i.invoice_id = invitem.invoice_id
However, this results in multiple duplicate rows of the quotation_item_amount and invoice_item_amount.
Have tried using UNION / UNION ALL and several other queries which just do not work.
Thank you for all your suggestions.
It looks like you are trying to aggregate along two different dimensions at the same time. The solution is to pre-aggregate along each dimension:
SELECT *
FROM data_client_project cp LEFT JOIN
(SELECT q.client_project_id,
SUM(qi.quotation_item_amount * qi.quotation_item_qty) as quot_amt
FROM quotation q JOIN
quotation_item qi
ON qi.quotation_id = q.quotation_id
GROUP BY q.client_project_id
) q
USING (client_project_id) LEFT JOIN
(SELECT i.client_project_id,
SUM(invoice_item_price) as inv_amt
FROM invoice i JOIN
invoice_item ii
ON i.invoice_id = ii.invoice_id
GROUP BY i.client_project_id
) i
USING (client_project_id);
Two notes about your style.
First, you are using arbitrary letters for table aliases. This makes the query quite hard to follow and becomes quite awkward if you add new tables, remove tables, or rearrange the names. Use abbreviations for the tables. Much easier to follow.
Second, I don't really recommend SELECT * for such queries. But, you can avoid duplicated column by replacing ON with USING.
I may be missing something, but your table descriptions do not include a example for data_client or data_client_project Given your example, I expect your row expansion is coming from the first 3 joins.
Make sure that the below is giving you the list of data you want first, then try joining in the calculation:
SELECT *
FROM data_client_project a
LEFT JOIN quotation b ON a.client_project_id = b.client_project_id
LEFT JOIN data_client d ON a.client_id = d.client_id
LEFT JOIN invoice i ON a.client_project_id = i.client_project_id;
#you may want to append the above with a limit 100 for testing.
if you have duplicated rows form the main query then add distinct for obatin a only distinct rows
and andd the where conditio for filtering the result by quotitem.quot_amt < invitem.inv_amt
SELECT distinct a.*, b.*, d.*, i.*
FROM data_client_project a
LEFT JOIN quotation b ON a.client_project_id = b.client_project_id
LEFT JOIN data_client d ON a.client_id = d.client_id
LEFT JOIN invoice i ON a.client_project_id = i.client_project_id
JOIN (
SELECT quotation_id,
SUM(c.quotation_item_amount * c.quotation_item_qty) as quot_amt
FROM quotation_item c
GROUP BY c.quotation_id
) quotitem ON b.quotation_id = quotitem.quotation_id
JOIN (
SELECT invoice_id,
SUM(e.invoice_item_price) as inv_amt
FROM invoice_item e
GROUP BY e.invoice_id
) invitem ON i.invoice_id = invitem.invoice_id
WHERE quotitem.quot_amt < invitem.inv_amt
I have two tables, 'authorization' and 'transaction'.
authorization table
Auth_ID | User_ID | Auth_Hours
-------------------------------
5 | 1 | 60
6 | 2 | 40
7 | 3 | 50
transaction table
Auth_ID | User_ID | Used_Hours
-------------------------------
5 | 1 | 5
6 | 2 | 2
5 | 1 | 20
6 | 2 | 17
5 | 1 | 11
6 | 2 | 9
I want my query to sum Used_Hours and group by Auth_ID and User_ID from the transaction table. Here's the catch - I also want the query result to show users who have not used any of their Auth_Hours with a 0. See below:
QUERY
SELECT a.Auth_ID, a.USER_ID, a.Auth_Hours, SUM(t.Used_Hours)
FROM AUTHORIZATION a
JOIN TRANSACTION t
on a.Auth_ID = t.Auth_ID
and a.User_ID = t.User_ID
GROUP BY a.Auth_ID, a.USER_ID, a.Auth_Hours
Actual Result
Auth_ID | User_ID | Auth_Hours | Total Hours Used
-------------------------------------------------
5 | 1 | 60 | 36
6 | 2 | 40 | 28
Wanted Result
Auth_ID | User_ID | Auth_Hours | Total Hours Used
-------------------------------------------------
5 | 1 | 60 | 36
6 | 2 | 40 | 28
7 | 3 | 50 | 0
I would imagine the query to be relatively simple.
The JOIN statement is a shortcut for INNER JOIN which returns records that have matching values in both tables. If you want to return all records from one table and the matching records from the other table, then you should use an outer join (LEFT [OUTER] JOIN or RIGHT [OUTER] JOIN). Then you can use the IFNULL() or COALESCE()functions to convert NULLs to zeros:
SELECT a.Auth_ID, a.USER_ID, a.Auth_Hours, IFNULL(SUM(t.Used_Hours), 0) AS 'Total Hours Used'
FROM authorization a
LEFT JOIN transaction t ON a.Auth_ID = t.Auth_ID AND a.User_ID = t.User_ID
GROUP BY a.Auth_ID, a.USER_ID, a.Auth_Hours
Notice that I used single quotes to assign a string with spaces as a name to the total field (as you used in your examples). This will work in all databases. In MySQL you can also use back ticks, but that only works in MySQL.
Here is a good illustration about the different types of joining tables.
this query work in any mysql Engine or Version. use this query:
SELECT a.Auth_ID, a.USER_ID, a.Auth_Hours, CASE COUNT(t.Used_Hours) WHEN 0 THEN 0 ELSE SUM(t.Used_Hours) END AS 'Total Hours Used'
FROM authorization a
LEFT JOIN TRANSACTION t ON a.Auth_ID = t.Auth_ID AND a.User_ID = t.User_ID
GROUP BY a.Auth_ID, a.USER_ID, a.Auth_Hours
I am trying to create a query that groups members into children and adults:
mysql tickets:
|ticket_id|ticket_no|
| 1 | 123456 |
mysql members:
|member_id|ticket_no| name | dob |
| 1 | 123456 | edward | 2010-03-05 | //child
| 2 | 123456 | karen | 1965-03-05 | //adult
html output:
|ticket_no|Adult Names|Children Names|
| 123456 | karen | edward |
I would like to do it with a join query really and output it in a loop as a row.
This is just an example:
SELECT t.*, m.*
FROM (`tickets` t)
JOIN(`members` m)
ON(m.`ticket_no` = t.`ticket_no`)
(SELECT m.`name` FROM m WHERE m.`dob` BETWEEN {$cStart} AND {$cEnd}) as children
(SELECT m.`name` FROM m WHERE m.`dob` BETWEEN {$aStart} AND {$aEnd}) as adults
ORDER BY t.`ticket_no`
Any help greatly appreciated.
Working SQL:
SELECT t.*,
CASE
WHEN m.`dob`
BETWEEN '{$datetime['start']}'
AND '{$datetime['end']}'
THEN m.`name`
END AS ChildName
FROM `tickets` t
JOIN (`members` m)
ON (m.`ticket_no` = t.`ticket_no`)
ORDER BY t.`ticket_no`
you can try this. Just pass appropriate date range in variables you are using
SELECT t.ticket_no,
case when m.`dob` BETWEEN {$aStart} AND {$aEnd} then m.name end as AdultName,
case when m.`dob` BETWEEN {$cStart} AND {$cEnd} then m.name end as ChildName
FROM (`tickets` t)
JOIN(`members` m)
ON(m.`ticket_no` = t.`ticket_no`)
ORDER BY t.`ticket_no`
I'm having a lot of difficulties in manipulating information in order to achieve the desired result. Through a query I join with the following result:
Area | ID_AT | AT | TYPE
-----------------------------------------
Informatica | 1 | Sistemaa | E
Informatica | 3 | engraçado | E
Informatica | 3 | engraçado | I
Gestão | 2 | aaaaa | I
query:
select a.Area, c.ID_AT, c.AT, dest.type
from AREA_AT a
left join AT_C c on a.id_AREA_AT = c.id_AREA_AT
left join dest_atv d on d.id_AT = c.id_at and d.id_uo = c.id_uo
left join CLIE dest on d.id_CLIE = dest.id_CLIE
where id.uo = 1222
order by a.id_AREA_AT, c.id_at, dest.type
But what I want is to create a table in php as follows:
Area | ID_AT | AT | E | I
-------------------------------------------
Informatica | 1 | Sistemaa | X |
Informatica | 3 | engraçado | X | X
Gestão | 2 | aaaaa | | X
In short, what I intend to do here is to show only one table ID ativ, showing that may exist or not type I or E depending on what comes from the query.
Do I have to modify the query to facilitate the work myself? Or will I have to create a fairly complicated algorithm in php to perform this function? Ando here turns hours and still could not find a solution that will can help me?
It sounds like you need to do a pivot query. Something like
select a.Area,
c.ID_AT,
c.AT,
max( case when dest.type = 'E' then 'X' else null end) E,
max( case when dest.type = 'I' then 'X' else null end) I
from AREA_AT a
left join AT_C c on a.id_AREA_AT = c.id_AREA_AT
left join dest_atv d on d.id_AT = c.id_at and d.id_uo = c.id_uo
left join CLIE dest on d.id_CLIE = dest.id_CLIE
where id.uo = 1222
group by a.area, c.id_at, c.at
order by a.area, c.id_at
Note that you won't be able to ORDER BY the a.id_area_at column if you are not selecting (and grouping by) that column in this formulation. I changed that to sort by a.area instead.
I need to calculate total points (cn_category.category_points) for every player (cn_players record). This is driven by the table cn_records, which contains both current and obsolete records. I need to sum only the best times for all categories (cn_category records), maps (cn_maps records; not shown below), and games (cn_wads records; not shown below). Also, for categories #9, #10, and #11, points are awarded for the three best scores/records, instead of just the very best.
I have this query so far, but it counts both current and obsolete records:
SELECT *, MIN(r.record_time), SUM(cn_category.category_points)
FROM (select * from cn_records as r order by r.record_time ASC) AS r
LEFT JOIN cn_players ON r.player1_id = cn_players.player_id
LEFT JOIN cn_category ON r.category_id = cn_category.category_id
WHERE r.category_id BETWEEN 1 AND 25
GROUP BY r.player1_id
ORDER BY SUM(category_points), r.player1_id DESC
(Note: The above is not the complete query. A few joins and WHERE-clause conditions have been removed for simplicity's sake.)
Categories are in the cn_category table:
category_id | category_points | ...
-------------+-----------------+-----
1 | 1.0 | ...
9 | 5.0 | ...
... | ... | ...
Players are in the cn_players table:
player_id | ...
-----------+-----
1 | ...
2 | ...
... | ...
"Records" are in the cn_records table (which has thousands of rows):
record_id | category_id | player1_id | record_time | ...
-----------+-------------+------------+-------------+-----
1 | 1 | 143 | 00:00:00 | ...
2 | 1 | 145 | 00:00:00 | ...
3 | 1 | 41 | 00:00:00 | ...
4 | 1 | 41 | 00:00:00 | ...
5 | 1 | 141 | 00:00:00 | ...
6 | 1 | 41 | 00:00:00 | ...
... | ... | ... | ... | ...
Something like this? You start with selecting the max points per category from the cn_records table. Then you can join the cn_records table again to select only the actual records per category (the ones that match the max points).
After that, you can join player and category details and do additional grouping and filtering as you like. The hard part is done. :)
SELECT
p.player_id,
p.player_name,
sum(c.category_points) AS total_points
FROM
/* Select the max points per category */
(SELECT
r.category_id,
min(r.record_time) AS best_time
FROM
cn_records r
GROUP BY
r.category_id) ar /* Actual records */
/* Join the records table to find the records that match the max points.
These are the only records to investigate */
INNER JOIN cn_records r
ON r.category_id = ar.category_id
AND r.record_time = ar.best_time
/* Inner (don't left) join player. What is a record without player */
INNER JOIN cn_players p ON r.player1_id = p.player_id
INNER JOIN cn_category c ON r.category_id = c.category_id
GROUP BY
r.category_id