I need a bit of help on a mathematical function,
Following query will result 2 lines per lot like this:
group | style | lot | section | q1 | q2 |q3 | q4 | ...
aaaaa | sssss | 123 | 111111 | 55 | 77 | 88 | 99 | ...
aaaaa | sssss | 123 | 222222 | 10 | 20 | 20 | 10 | ...
aaaaa | sssss | 321 | 111111 | 11 | 22 | 44 | 55 | ...
aaaaa | sssss | 321 | 222222 | 10 | 23 |33 | 10 | ...
each lot result 2 diff section code (2 lines)
Question is : how do I make a subtraction between the 2 section code for the colums q1 q2 q3 q4 q5 ... ?
expected results:
group | style | lot | q1 | q2 |q3 | q4 | ...
aaaaa | sssss | 123 | 45 | 57 |68 | 89 | ...
aaaaa | sssss | 321 | 1 | -1 |11 | 45 | ...
query so far :
SELECT DISTINCT gp_style_gr.code_groupe, po_lot.num_style, po_lot_sp.Num_lot,
po_lot_sp.num_secti, po_lot_se.code_secti, po_lot.terminer, po_lot.date_livraison,
po_lot_sp.qte_1, po_lot_sp.qte_2, po_lot_sp.qte_3, po_lot_sp.qte_4, po_lot_sp.qte_5,
po_lot_sp.qte_6, po_lot_sp.qte_7, po_lot_sp.qte_8, po_lot_sp.qte_9, po_lot_sp.qte_10,
po_lot_sp.qte_11, po_lot_sp.qte_12, po_lot_sp.qte_13, po_lot_sp.qte_14, po_lot_sp.qte_15,
po_lot_sp.qte_16, po_lot_sp.qte_17, po_lot_sp.qte_18, po_lot_sp.qte_19, po_lot_sp.qte_20,
po_lot_sp.qte_21, po_lot_sp.qte_22, po_lot_sp.qte_23, po_lot_sp.qte_24, po_lot_sp.qte_25,
po_lot_sp.qte_26, po_lot_sp.qte_27, po_lot_sp.qte_28, po_lot_sp.qte_29, po_lot_sp.qte_30
FROM po_lot_sp
LEFT OUTER JOIN po_lot_se ON po_lot_se.num_lot = po_lot_sp.num_lot
and po_lot_se.num_secti = po_lot_sp.num_secti
LEFT OUTER JOIN po_lot ON po_lot.num_lot = po_lot_sp.num_lot
LEFT OUTER JOIN gp_style_gr ON gp_style_gr.num_style = po_lot.num_style
WHERE
((gp_style_gr.code_groupe = 'INSTOCK') and (po_lot.terminer = '0')
and (po_lot_se.code_secti = '01')) or ((gp_style_gr.code_groupe = 'INSTOCK')
and (po_lot.terminer = '0') and (po_lot_se.code_secti = '09'))
ORDER BY gp_style_gr.code_groupe, po_lot.num_style, po_lot_sp.Num_lot,
po_lot_sp.num_secti, po_lot_se.code_secti, po_lot.terminer, po_lot.date_livraison,
Thanks !
If the section code follows some pattern as it does in your example, then you can simply join the table against itself.
I'll pretend your table is called po_lot_sp as it is in your example.
In the following query, I assume that the second row has a higher section number. That's the condition t1.section > t2.section. If not, change appropriately. If the section numbers follow no pattern, then ignore this completely.
SELECT t1.`group`, t1.style, t1.lot, t1.section,
t2.q1 - t1.q1 q1, t2.q2 - t1.q2 q2, t2.q3 - t1.q3 q3, t2.q4 - t1.q4 q4
FROM t t1
JOIN t t2 ON t1.`group` = t2.`group` AND t1.style = t2.style AND
t1.lot = t2.lot AND t1.section > t2.section
Fiddle here.
This is the fastest way I can think of. Of course, this is assuming that you want to decrement from section with value '111111':
SELECT `group`, style, lot,
sum(if(section = '111111', q1, -q1)),
sum(if(section = '111111', q2, -q2)),
sum(if(section = '111111', q3, -q3)),
sum(if(section = '111111', q4, -q4))
FROM t
GROUP BY `group`, style, lot
Fiddle here.
By the way, try not to use group as a column name. It is a reserved word.
If you don't know the section value you want to decrement from and you only want to decrement from the lowest section, then go for Andy's solution.
Try this:
select a.group,a.style,a.lot,
coalesce(a.q1 -
(select b.q1 from tablename b where b.ID = a.ID + 1), a.q1) as q1,
coalesce(a.q2-
(select b.q2 from tablename b where b.ID = a.ID + 1), a.q2) as q2,
coalesce(a.q3-
(select b.q3 from tablename b where b.ID = a.ID + 1), a.q3) as q3,
coalesce(a.q4-
(select b.q4 from tablename b where b.ID = a.ID + 1), a.q4) as q4
from tablename a group by a.lot
Note: Here ID refers to a primary key from your table and tablename refers to your original table name.So replace the field ID with your primary key field and table name vice-versa.
Demo
Related
I have some similar selects that I'd like to join and aggregate values.
I've already tried with join, inner join, union, union all, without the expected result.
See that the selects are very similar. The differences are:
SUM(comumn_02)*.05 AS SUM_01 different from SUM(comumn_02)*.45 AS SUM_02
and AND COLUMN_05 = ALFA different from AND COLUMN_05 = BETA in the second select.
The whole thing is here:
SELECT
COLUMN_01,
SUM(COLUMN_02)*.05 AS SUM_01
FROM table
WHERE
COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = ALFA
GROUP BY COLUMN_01
UNION ALL
SELECT
COLUMN_01,
SUM(COLUMN_02)*.45 AS SUM_02
FROM table
WHERE
COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = BETA
GROUP BY COLUMN_01;
In this example we have the following result:
|-----------|--------|
| COLUMN_01 | SUM_01 |
|-----------|--------|
| value_01 | 465 |
| value_02 | 186 |
| value_03 | 245 |
| value_01 | 102 |
| value_02 | 108 |
| value_03 | 325 |
|--------------------|
But what I'd like to have would be:
|-----------|--------|
| COLUMN_01 | SUM_01 |
|-----------|--------|
| value_01 | 567 | //sum of 465 + 102
| value_02 | 294 | //sum of 186 + 108
| value_03 | 570 | //sum of 245 + 570
|--------------------|
And most important, would this be scalable? i.e. to use it with three or more select "unions" or the performance would decrease a lot?
The problem in your query is that you're aggregating on the single tables, then you are using UNION ALL to get your values. In order to solve your problem, you need to invert the order of these two operations:
first you apply the UNION function
then you aggregate with the SUM aggregation function
WITH CTE AS(
SELECT COLUMN_01,
COLUMN_02*.05 AS COLUMN
FROM table
WHERE COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = ALFA
UNION ALL
SELECT COLUMN_01,
COLUMN_02*.45 AS COLUMN
FROM table
WHERE COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = BETA
)
SELECT COLUMN_01,
SUM(COLUMN) AS SUM_01
FROM CTE
GROUP BY COLUMN_01
Use conditional aggregation:
SELECT COLUMN_01,
SUM(COLUMN_02 * CASE COLUMN_05 WHEN 'ALFA' THEN 0.05 WHEN 'BETA' THEN 0.45 END) AS SUM_01
FROM tablename
WHERE COLUMN_03 = 1 AND COLUMN_04 = 2 AND (COLUMN_05 IN ('ALFA', 'BETA'))
GROUP BY COLUMN_01;
I would like to calculate the Click-Through Ratio (CTR) of several articles of a website using SQL.
The formula of the CTR is CTR = number clicks / number impressions, i.e. a ratio of how many times an article has been clicked and how many times it has been shown.
I have two tables:
´article_click´: A table with several columns, namely ´article_id´ (denoting the id of the article), ´description´ (a brief description of the article), ´timestamp´ (when it has been clicked), among others. Every time a user clicks an article, a new row is created in the table.
´article_impression´: Similarly, a table with several columns, namely ´article_id´ (denoting the id of the article), ´description´ (a brief description of the article), ´timestamp´ (when it has been shown), among others. Every time an article is shown to a user, a new row is created in the table.
Both tables 1 and 2 look like this:
+------------+-------------+------------------+-----+
| article_id | description | timestamp | ... |
+------------+-------------+------------------+-----+
| 102 | Potatoe | 2021-01-01 13:45 | ... |
| 11 | Lettuce | 2020-02-11 11:00 | ... |
| 34 | Train | 2019-12-12 09:31 | ... |
| 21 | Car | 2011-11-11 08:32 | ... |
| 201 | Train | 2014-02-10 02:12 | ... |
| ... | ... | ... | ... |
+------------+-------------+------------------+-----+
And I would like to create a table such that:
+------------+-----+
| article_id | CTR |
+------------+-----+
| 11 | 0.4 |
| 23 | 0.6 |
| 34 | 0.2 |
| 44 | 0.8 |
| 45 | 0.3 |
| ... | ... |
+------------+-----+
In order to do so, I have tried:
SELECT article_click.article_id, COUNT(article_click.article_id) / COUNT(article_impression.article_id) AS CTR
FROM article_click
INNER JOIN article_impression ON article_click.article_id = article_impression.article_id
GROUP BY article_click.article_id DESC;
But I obtain something like:
+------------+-----+
| article_id | CTR |
+------------+-----+
| 11 | 1.0 |
| 23 | 1.0 |
| 34 | 1.0 |
| 44 | 1.0 |
| 45 | 1.0 |
| ... | ... |
+------------+-----+
Can anyone spot the mistake here? I'm using MySQL as RDBMS.
If the click-through-rate (CTR) is number clicks / number impressions then you'll need to calculate the number of clicks on an article and the number of impressions on an article before joining them to perform the calculation.
You could do this with subqueries or CTEs, but I've opted for the former here.
SELECT c.article_id, c.click_count / i.impression_count AS CTR
FROM (
SELECT article_id, COUNT(*) AS click_count
FROM article_click
GROUP BY article_id) AS c
INNER JOIN (
SELECT article_id, COUNT(*) AS impression_count
FROM article_impression
GROUP BY article_id) AS i
ON c.article_id = i.article_id;
Try it out on SQL Fiddle.
Note that using an INNER JOIN will exclude articles that have impressions but no clicks, so you won't get results where the CTR is 0. If you want those, you can use a LEFT JOIN from impressions to clicks. Since an article cannot be clicked if it has not been shown, we know that a LEFT JOIN from impressions to clicks is sufficient to show all data.
SELECT i.article_id, COALESCE(c.click_count, 0) / i.impression_count AS CTR
FROM (
SELECT article_id, COUNT(*) AS impression_count
FROM article_impression
GROUP BY article_id) AS i
LEFT JOIN (
SELECT article_id, COUNT(*) AS click_count
FROM article_click
GROUP BY article_id) AS c
ON i.article_id = c.article_id;
Note that we have to use the article_id from article_impression since article_click might be NULL. For the same reason, we have to COALESCE the click_count so that we don't end up with an error trying to divide NULL.
Before using joins duplicate data must be avoided. Get individual counts of each table and join both the queries.
select a.article_id, article_click/article_impression_click as ctr
from ( select a.article_id, count(a.article_id) article_click from
article_click a group by article_id) a inner join (select
a.article_id, count(a.article_id) article_impression_click from
article_impression a group by article_id) b on
a.article_id=b.article_id
WITH
v_article AS
( SELECT 'S' type, article_impression.id FROM article_impression
UNION ALL
SELECT 'C' type, article_click.id FROM article_click
)
SELECT
v_article.id,
COUNT(CASE WHEN v_article.type = 'C' THEN 1 END) nb_show,
COUNT(CASE WHEN v_article.type = 'S' THEN 1 END) nb_click,
CASE
WHEN COUNT(CASE WHEN v_article.type = 'S' THEN 1 END) > 0 THEN
ROUND(COUNT(CASE WHEN v_article.type = 'C' THEN 1 END) / COUNT(CASE WHEN v_article.type = 'S' THEN 1 END), 2)
END ratio_click_show
FROM v_article
GROUP BY
v_article.id
;
If you're sure an article can be click only if it has been previously shown (nb_show > 0 and nb_show > nb_click), you can remove the CASE around the ratio calculation.
I am trying to do this query:
SELECT
A.*
, (SELECT MAX(B.Date2) FROM Tab2 B WHERE A.ID = B.ID AND A.Date > B.Date2) AS MaxDate
FROM
Tab A
This works but it takes a lot of time to run when you have a lot of rows. Is there any quicker way to do this which give the same results?
Thank you!
Edit:
The table définitions are as follow:
Tab : (dd-mm-yyyy)
ID | Date
1 | 19-01-2018
1 | 14-01-2018
2 | 18-02-2019
3 | 20-03-2019
Tab2:
ID | Date2
1 | 10-01-2018
1 | 15-01-2018
1 | 20-01-2018
2 | 15-02-2019
2 | 21-02-2019
3 | 25-03-2019
I want my query returns:
ID | Date | MaxDate
1 | 19-01-2018 | 15-01-2018
1 | 14-01-2018 | 10-01-2018
2 | 18-02-2019 | 15-02-2019
3 | 20-03-2019 | NULL
Thanks!
It was unexpected for me but this query worked:
SELECT
A.ID
, A.Date
, MAX(B.Date2) AS MaxDate
FROM
Tab A
left outer join Tab2 B
on A.ID = B.ID and A.Date > B.Date2
GROUP BY
A.ID, A.Date
;
I didn't know that we can put a column from a table in a group by when the column of the MAX() is in another table.
I have two tables: tblBusinesses, tblBusinessImages that are matched on tblBusinesses.fldID = tblBusinessImages.fldBusinessID like this:
tblBusinesses
=============
fldID | fldName | fldTitle | fldBody
-------------------------------------
1 | b1 | title1 | body1
2 | b2 | title2 | body2
3 | b3 | title3 | body3
4 | b4 | title4 | body4
tblBusinessImages
=============
fldID | fldFileName | fldTitle | fldBusinessID | fldOrder
-----------------------------------------------------------
1 | img1.jpg | img1title | 1 | 3
2 | img2.jpg | img2title | 1 | 1
3 | img3.jpg | img3title | 1 | 2
I want to write a query that gets for every business in tblBusinesses, the image in tblBusinessImages with minimal fldOrder. In other words, in the example I wrote above I want to get business b1, title1, body1 along with img2.jpg from tblBusinessImages (because it has minimal fldOrder in tblBusinessImages ).
Any help would be appreciated!
If fldOrder alwyas starts at one for each fldBusinessID the query becomes
SELECT b.fldName, b.fldTitle, b.fldBody, i.fldFileName
FROM
tblBusinesses b
LEFT JOIN tblBusinessImages i
ON (b.fldID = i.fldBusinessID AND i.fldOrder = 1)
If not, you will have to use subqueries.
SELECT b.fldName, b.fldTitle, b.fldBody, firstImage.fldFileName
FROM
tblBusinesses b
LEFT JOIN (
SELECT i.fldBusinessID, i.fldFileName
FROM
tblBusinessImages i
WHERE
i.fldOrder = (SELECT MIN(x.fldOrder)
FROM tblBusinessImages x
WHERE x.fldBusinessID = i.fldBusinessID)
) firstImage
ON b.fldID = firstImage.fldBusinessID
Note that the left join makes the query return also businesses having no image at all.
If you need only entries having images where the smallest order id can have any value:
SELECT b.fldName, b.fldTitle, b.fldBody, i.fldFileName
FROM
tblBusinesses b
INNER JOIN tblBusinessImages i
ON b.fldID = i.fldBusinessID
WHERE
i.fldOrder = (SELECT MIN(x.fldOrder)
FROM tblBusinessImages x
WHERE x.fldBusinessID = i.fldBusinessID)
Do you only want to get the ONE entry with the smallest fldOrder?
Didn't test this query:
SELECT b.fldName,b.fldTitle,b.fldBody,bi.fldFileName,bi.fldOrder
FROM tblBuisnesses b
INNER JOIN tblBuisnessImages bi ON bi.fldBusinessID=b.fldID ORDER BY b.fldOrder DESC LIMIT 1;
Update #1: query gives me syntax error on Left Join line (running the query within the left join independently works perfectly though)
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance'
FROM MyTable b1
JOIN CustomerInfoTable c on c.id = b1.company_id
#Filter for Clients of particular brand, package and active status
where c.brand_id = 2 and c.status = 2 and c.package_id = 3
LEFT JOIN
(
SELECT b2.company_id, sum(b2.debit) as 'Current_Usage'
FROM MyTable b2
WHERE year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
)
b3 on b3.company_id = b1.company_id
group by b1.company_id;
Original Post:
I keep track of debits and credits in the same table. The table has the following schema:
| company_id | timestamp | credit | debit |
| 10 | MAY-25 | 100 | 000 |
| 11 | MAY-25 | 000 | 054 |
| 10 | MAY-28 | 000 | 040 |
| 12 | JUN-01 | 100 | 000 |
| 10 | JUN-25 | 150 | 000 |
| 10 | JUN-25 | 000 | 025 |
As my result, I want to to see:
| Grouped by: company_id | Balance* | Current_Usage (in June) |
| 10 | 185 | 25 |
| 12 | 100 | 0 |
| 11 | -54 | 0 |
Balance: Calculated by (sum(credit) - sum(debits))* - timestamp does not matter
Current_Usage: Calculated by sum(debits) - but only for debits in JUN.
The problem: If I filter by JUN timestamp right away, it does not calculate the balance of all time but only the balance of any transactions in June.
How can I calculate the current usage by month but the balance on all transactions in the table. I have everything working, except that it filters only the JUN results into the current usage calculation in my code:
SELECT b.company_id, ((sum(b.credit)-sum(b.debit))/1024/1024/1024/1024) as 'BW_remaining', sum(b.debit/1024/1024/1024/1024/28*30) as 'Usage_per_month'
FROM mytable b
#How to filter this only for the current_usage calculation?
WHERE month(a.timestamp) = 'JUN' and a.credit = 0
#Group by company in order to sum all entries for balance
group by b.company_id
order by b.balance desc;
what you will need here is a join with sub query which will filter based on month.
SELECT T1.company_id,
((sum(T1.credit)-sum(T1.debit))/1024/1024/1024/1024) as 'BW_remaining',
MAX(T3.DEBIT_PER_MONTH)
FROM MYTABLE T1
LEFT JOIN
(
SELECT T2.company_id, SUM(T2.debit) T3.DEBIT_PER_MONTH
FROM MYTABLE T2
WHERE month(T2.timestamp) = 'JUN'
GROUP BY T2.company_id
)
T3 ON T1.company_id-T3.company_id
GROUP BY T1.company_id
I havn't tested the query. The point here i am trying to make is how you can join your existing query to get usage per month.
alright, thanks to #Kshitij I got it working. In case somebody else is running into the same issue, this is how I solved it:
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance',
(
SELECT sum(b2.debit)
FROM MYTABLE b2
WHERE b2.company_id = b1.company_id and year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
) AS 'Usage_June'
FROM MYTABLE b1
#Group by company in order to add sum of all zones the company is using
group by b1.company_id
order by Usage_June desc;