Obtaining information from 4 tables and manipulate it? - php

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.

Related

MySQL SUM of multiple rows from multiple table

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

Select distinct on multiple columns with where statement

I want to select each available entry for each column once. This problem was solved with
SELECT DISTINCT a from my_table
UNION
SELECT DISTINCT b from my_table
UNION
SELECT DISTINCT c from my_table
UNION
SELECT DISTINCT d from my_table
in this question: MySQL SELECT DISTINCT multiple columns
I want to go further and use the same WHERE statements on each subquery. Is there any way without defining the WHERE each time? My current query would look like this:
SELECT DISTINCT a from my_table WHERE a='a' AND b=1 AND c='.' AND d='ab'
UNION
SELECT DISTINCT b from my_table WHERE a='a' AND b=1 AND c='.' AND d='ab'
UNION
SELECT DISTINCT c from my_table WHERE a='a' AND b=1 AND c='.' AND d='ab'
UNION
SELECT DISTINCT d from my_table WHERE a='a' AND b=1 AND c='.' AND d='ab'
All parameters don't have to be given, I just want to show the maximum that has to be possible. Is there any way to write this shorter?
I use PHP with doctrine, if that is any help.
Thanks in advance!
Example:
my_table:
a | b | c | d
-----+-----+-----+-----
a | 0 | . | ab
b | 0 | - | ag
a | 1 | . | cfd
c | 1 | . | b
a | 1 | - | ab
c | 1 | - | cfd
should give this result (without where statement):
a | b | c | d
-----+-----+-----+-----
a | 0 | . | ab
b | 1 | - | ag
c | | | cfd
| | | b
And with WHERE b=0 statement:
a | b | c | d
-----+-----+-----+-----
a | 0 | . | ab
b | | - | ag
EDIT: changed subqueries to UNION and made the data types fit to the example
UPDATE: Well, I originally wrote up a generic SQL solution for this problem, not realizing that MySQL apparently doesn't allow for it.
So if you can create a view, that may be the lightest-weight solution. (The view's defining query would be the same as the select in my original solution's with clause.)
Alternately you could create a temporary table. Maybe a little more resource-intensive if there's much data, but less likely that anyone would restrict the required permissions.
For the record, original solution was as follows:
with my_filtered as (
select *
from my_table
where a = 1 and b = 2 -- and ...
)
-- carry on with your query, using my_filtered instead of my_table

how do i select data from multiple columns from two tables where there is a common column in both?

seems like a cake walk, because of the common column thing, but i've looked and i've looked and can't find the answer anywhere. there may be very similar things people are trying to do, but everyone always needs it slightly different than how i need it. anyway here goes...
jobs table
+----+--------+----+----+----+
| id | userid | i1 | i2 | i3 |
+----+--------+----+----+----+
| 1 | 1 | a | k | t |
| 2 | 1 | b | l | u |
| 3 | 1 | c | m | v |
| 4 | 2 | d | n | w |
| 5 | 2 | f | o | x |
| 6 | 2 | g | p | y |
| 7 | 3 | h | q | z |
| 8 | 3 | i | r | a |
| 9 | 4 | j | s | b |
+----+--------+----+----+----+
user_table table
+--------+----+----+----+
| userid | fn | ln | i4 |
+--------+----+----+----+
| 1 | a | b | w |
| 2 | c | d | x |
| 3 | e | f | y |
| 4 | g | h | z |
+--------+----+----+----+
i want to select multiple columns from one table and multiple columns from another table, and the amount of columns don't have the same amount. so if i'm selecting id, i1, i2, and i3 from jobs table and selecting fn, ln, and i4 from user_table table, then that means i'm selecting 4 pieces of info from jobs table and 3 pieces of info from user_table table.
so let's say i want to select a specific job and display the info for that job, but also i want to display the info of the user that belongs to the job then it might go something like this...
job 1:
id: 1, i1: a, i2: k, i3: t, fn: a, ln: b, i4: w
for job 4 it would be:
id: 4, i1: d, i2: n, i3: w, fn: c, ln: d, i4: x
the jobs table and user_table table have the common column of userid. so how do i write my query to do the above using this common column?
i tried all sorts of things with SELECT and WHERE and AND and JOIN and UNION and AS and GROUP and BY and writing the table names separate from the column names and writing the table names and column names together with a period in between. i just can't find the correct way to write this query.
in these examples below that don't work i'm just trying to select one thing from the user_table table and everything from the jobs table that isn't the userid, but i will need the ability to select multiple things from the 2nd table if needed. so i want to select everything from first table and just certain things from second table.
doesn't work:
SELECT id, i1, i2, i3, i4 FROM jobs, user_table WHERE jobs.id = $id GROUP BY id
doesn't work:
SELECT * from jobs where id = $id UNION SELECT i4 from user_table where userid = $userid
The query
SELECT * FROM jobs
INNER JOIN user_table on jobs.userid = user_table.userid
will join your jobs information with the user information. Then just add your where clause:
SELECT * FROM jobs
INNER JOIN user_table on jobs.userid = user_table.userid
where jobs.id = $id
You want to join the tables like so:
SELECT j.column1, j.column2, u.column5, u.column6
FROM
jobs j
INNER JOIN
user_table u
ON
u.userid = j.userid
WHERE
j.id = $id
try
SELECT id, i1, i2, i3, i4 FROM jobs
inner join user_table on jobs.id = user_table.id WHERE jobs.id = $id GROUP BY id

SQL - Selecting using only the first result of the INNER JOIN

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

Join table on multiple ids

exp table: This is where I store entries while keeping the domains and sub domains in a separate table. The sub_domain column can have one or multiple ids that match the id column in misc.
+----+---------+-----------+------------+-----------+--------+-------------+------------+------------+
| id | user_id | job_type | experience | exp_title | domain | sub_domain | start | end |
+----+---------+-----------+------------+-----------+--------+-------------+------------+------------+
| 83 | 268 | Full Time | dasdfg | dest | 76 | 89,91,93,95 | 07/15/2012 | 07/31/2012 |
| 84 | 269 | Full Time | abcdef | title | 77 | 89 | 07/15/2011 | 07/31/2011 |
+----+---------+-----------+------------+-----------+--------+-------------+------------+------------+
misc table:
+-----+----------------------------------------+--------------+
| id | name | category |
+-----+----------------------------------------+--------------+
| 89 | Name1 | category |
| 91 | Name2 | category |
| 93 | Name3 | category |
| 95 | Name4 | category |
| 55 | Name5 | category |
I was wondering how to change LEFT JOIN misc c ON a.sub_domain=c.id if there are more than one sub_domains in the exp table while keeping in mind that there can be one id as well.
$query_str = "SELECT a.*, b.name, c.name AS sub_name
FROM exp a
LEFT JOIN misc b ON a.domain=b.id
LEFT JOIN misc c ON a.sub_domain=c.id
WHERE a.user_id = ?";
It's not good to have columns with comma separated values. Read the answer on the question:
Is storing a comma separated list in a database column really that bad?
Short answer is: It's really that bad.
Until you fix the design, you can use the FIND_IN_SET() function:
$query_str = "
SELECT a.*, b.name, c.name AS sub_name
FROM exp a
LEFT JOIN misc b ON a.domain = b.id
LEFT JOIN misc c ON FIND_IN_SET(c.id, a.sub_domain)
WHERE a.user_id = ?
";
Try this
$query_str =
"SELECT a.*, b.name, c.name AS sub_name
FROM exp a
LEFT JOIN misc b ON a.domain=b.id
LEFT JOIN misc c ON locate(concat('','',c.id ,'',''),'','',a.sub_domain,'','') >0
WHERE a.user_id = ?";
You'd have to do this in two queries. One of the main reasons is that if ever you manage to accomplish what you want to do, you'd be returning rows with practically the same data except for the sub_name column. That's really unnecessary.
You can first JOIN the two tables on a.domain = b.id and then construct a second query that uses an IN(...) statement for all the subdomain values. It'd be something like this:
$subdomains = explode(',', $data_from_first_query['sub_domain']);
if (count($subdomains)) {
$q2 = '
SELECT
b.name
FROM
misc AS b
WHERE b.id IN ('.implode(',',array_fill(0, count($subdomains), '?')).')';
}
Then use foreach to bind values in your placeholder.
But then again, it is good practice to make sure your database is normalized.

Categories