I have join query which is not returning the result as expected. Below is the table structure, query used, result and expected result
Table A:
Id Name Token
1 A abcdef
2 B think
3. C Bxjscmsdnj
Table B:
id TableA_id configKey configValue
1 2 pmt ins
2 2 vat gas
3 1 vat nnnb
4 1 pmt mc
5 3 vat nhu
6 3 pmt nnu
7 2 hit bxhsjab
Below is the query that I used:
SELECT A.Token,
A.Name,
CASE
WHEN B.configKey = 'pmt’ THEN B.configValue
ELSE ''
END AS ‘PMT’,
CASE
WHEN B.configKey = ‘vat’ THEN B.configValue
ELSE ''
END AS ‘VAT’
FROM TABLEA A
INNER JOIN TABLEB B ON A.Id = B.TableA_id
WHERE B.configKey IN (‘PMT’, ‘VAT’)
ORDER BY A.id DESC;
Result:
Token Name PMT VAT
1 A nnnb
1 A mc
2 B gas
2 B ins
Expected Result:
Token Name PMT VAT
1 A mc nnnb
2 B ins gas
You want conditional aggregation. First, add a group by clause that groups together rows having the same id and name; then, wrap the case expressions in an aggregate function such as max():
select
a.id,
a.name,
max(case when b.configkey = 'pmt' then b.configvalue end) pmt,
max(case when b.configkey = 'vat' then b.configvalue end) vat
from tablea a
inner join tableb b on a.id = b.tablea_id
where b.configkey in ('pmt', 'vat')
group by a.id, a.name
order by a.id desc;
Related
I have 3 tables :
account
id
name
1
Google
2
Apple
custom_field
id
name
1
Phone
2
Email
custom_field_submission
id
custom_field_id
entity_id
value
1
1
1
555-444-333
2
1
2
111-111-111
3
2
1
google#goog.com
4
2
2
apple#apple.com
Expected result after query
id
name
Phone
Email
1
Google
555-444-333
google#goog.com
2
Apple
111-111-111
apple#apple.com
I have a query like this :
SELECT
a.id,
a.name,
phone.value as phone,
email.value as email
FROM account a
LEFT JOIN (
SELECT DISTINCT custom_field_submission.value, custom_field_submission.entity_id
FROM custom_field_submission
WHERE custom_field_submission.custom_field_id = 1) AS phone
ON phone.entity_id = a.id
LEFT JOIN (
SELECT DISTINCT custom_field_submission.value, custom_field_submission.entity_id
FROM custom_field_submission
WHERE custom_field_submission.custom_field_id = 2) AS email
ON email.entity_id = a.id
In the reality I have 20 custom fields for 10 000 accounts. Where I run the query It is very slow (3-4 seconds)
Do you have an idea to manage optimize this ?
Thanks.
What you need here is a pivot query:
SELECT
a.id,
a.name,
MAX(CASE WHEN cf.name = 'Phone' THEN cfs.value END) AS Phone,
MAX(CASE WHEN cf.name = 'Email' THEN cfs.value END) AS Email
FROM account a
LEFT JOIN custom_field_submission cfs
ON cfs.entity_id = a.id
LEFT JOIN custom_field cf
ON cf.id = cfs.custom_field_id
GROUP BY
a.id,
a.name;
So I have 3 tables: tableA, tableB, tableC with structures as follows
tableA
id
name
state
1
Ray
MD
2
Sam
LA
3
John
NY
tableB
id
a_id
amount
code
1
2
10
CHARGE
2
2
20
CHARGE
3
3
70
CHARGE
tableC
id
a_id
amount
code
1
2
50
CHARGE
2
2
40
DEPOSIT
3
1
60
CHARGE
I need the output of the join as follows:
A id
amount
1
60
2
30
3
70
So, here it calculates the sum of amount based on id in tableA. It checks the tableB for amount and if not present only then it checks tableC. Thats why the id 2 has amount 30 in output.
But what actually happens is the sum of both tables are added. So I get amount 120 for id 2. How do I get the required output?
So I tried this query here
Select if( SUM(CASE WHEN B.code != 'DEPOSIT'
THEN B.amount
ELSE 0 END) > 0,
SUM(CASE WHEN B.code != 'DEPOSIT'
THEN B.amount
ELSE 0 END),
SUM(CASE WHEN C.code != 'DEPOSIT'
THEN C.amount
ELSE 0 END)) as total
FROM tableA as A
left join tableB AS B on A.id=B.a_id
LEFT JOIN tableC AS C on A.id=C.a_id
GROUP BY A.id
You can try a solution like this
Select A.id, SUM(if(B.amount is NOT NULL,B.amount, C.amount)) as total
FROM tableA as A
LEFT JOIN tableB AS B on A.id=B.a_id and B.code != 'DEPOSIT'
LEFT JOIN tableC AS C on A.id=C.a_id and C.code != 'DEPOSIT'
GROUP BY A.id
Test this:
SELECT id, CASE WHEN b.amount
THEN b.amount
ELSE c.amount
END amount
FROM tableA a
LEFT JOIN ( SELECT a_id id,
SUM( CASE WHEN code != 'DEPOSIT'
THEN amount
ELSE 0
END ) amount
FROM tableB
GROUP BY id ) b USING (id)
LEFT JOIN ( SELECT a_id id,
SUM( CASE WHEN code != 'DEPOSIT'
THEN amount
ELSE 0
END ) amount
FROM tableC
GROUP BY id ) c USING (id)
I need to count how many id_no does have ALL line_number with test_activity = approved and test_status = completed and filtered by work_type which is B.
id id _no line_number work_type date test_activity test_status
P P-01 1 b 2020-02-02 approved completed
P P-01 2 b 2020-02-02 approved completed
P P-01 3 b 2020-02-02 approved completed
P P-02 4 b 2020-02-02 approved incompleted
P P-02 5 b 2020-02-02 approved incompleted
P P-02 6 b 2020-02-02 approved completed
Q Q-01 7 b 2020-02-02 in progress incompleted
Q Q-01 8 b 2020-02-02 in progress incompleted
Q Q-01 9 b 2020-02-02 not started incompleted
Q Q-02 10 b 2020-02-02 approved completed
Q Q-02 11 b 2020-02-02 approved completed
Q Q-02 12 b 2020-02-02 approved completed
I tried:
select date, count(*) as count
from (select id, id_no, date
from pt_pretest pp where work_type = 'b'
group by id, id_no, date
having min(test_activity) = max(test_activity) and min(test_activity ) = 'approved'
and having min(test_status) = max(test_status) and min(test_status) = 'completed'
) as x group by date
But it did not work. From the sample above, the expected result is:
id date count
P 2020-02-02 1
Q 2020-02-02 1
How should I write my query so I will the expected result?
Thankyou
There is one having too many in your query. Remove it.
select id, id_no, date, count(*)
from pt_pretest pp
where work_type = 'b'
group by id, id_no, date
having min(test_activity) = max(test_activity) and min(test_activity ) = 'approved'
and min(test_status) = max(test_status) and min(test_status) = 'completed'
order by id, id_no, date;
The following returns all ids that have at least one id_no that fails the condition:
select id, count(*)
from (select id, id_no
from t
where not (test_activity = 'approved' and test_status = 'completed')
group by id, id_no
) ii
group by id;
If you want zeros in the result set, use conditional aggregation in the subquery:
select id, count( num_bad > 0 )
from (select id, id_no,
sum( not (test_activity = 'approved' and test_status = 'completed') ) as num_bad
from t
group by id, id_no
) ii
group by id;
Going off of my last question: Complex Grouping in SQL Query…
In each grouping, I'd like to grab only the row with the highest 'step' value.
This is the query we came up with in the last question:
SELECT a.*, b.*
FROM (
SELECT request_id
FROM tableA
GROUP BY request_id
HAVING MAX(page_views) <= 0 AND MAX(step) <= 2
) AS sumQ
INNER JOIN tableA AS a ON sumQ.request_id = a.request_id
INNER JOIN tableB AS b ON a.request_id = b.id
That returns:
id request_id page_views step name phone
----------------------------------------------------------------
8 3 0 0 Jacob Clark 434-343-434
9 3 0 1 Jacob Clark 434-343-434
10 4 0 0 Alex Smith 222-112-2112
11 4 0 1 Alex Smith 222-112-2112
12 4 0 2 Alex Smith 222-112-2112
Which is what I wanted, however, I realized that in each group (group by request_id) I only need the row with the highest 'step' value. How can I modify my existing query to return only:
id request_id page_views step name phone
----------------------------------------------------------------
9 3 0 1 Jacob Clark 434-343-434
12 4 0 2 Alex Smith 222-112-2112
?
Then include step in the logic:
SELECT a.*, b.*
FROM (SELECT request_id, MAX(step) as maxstep
FROM tableA
GROUP BY request_id
HAVING MAX(page_views) <= 0 AND MAX(step) <= 2
) sumQ INNER JOIN
tableA a
ON sumQ.request_id = a.request_id AND
sumQ.maxstep = a.step INNER JOIN
tableB b
ON a.request_id = b.id;
Have you tried setting an ORDER BY on the step field and then take the TOP record?
SELECT a.*, b.*
FROM (
SELECT TOP 1 request_id
FROM tableA
GROUP BY request_id
HAVING MAX(page_views) <= 0 AND MAX(step) <= 2
ORDER BY step DESC
) AS sumQ
INNER JOIN tableA AS a ON sumQ.request_id = a.request_id
INNER JOIN tableB AS b ON a.request_id = b.id
I want to count how many times each user has rows within '5' of eachother.
For example, Don - 501 and Don - 504 should be counted, while Don - 501 and Don - 1600 should not be counted.
Start:
Name value
_________ ______________
Don 1235
Don 6012
Don 6014
Don 6300
James 9000
James 9502
James 9600
Sarah 1110
Sarah 1111
Sarah 1112
Sarah 1500
Becca 0500
Becca 0508
Becca 0709
Finish:
Name difference_5
__________ _____________
Don 1
James 0
Sarah 2
Becca 0
Use the ABS() function, in conjunction with a self-join in a subquery:
So, something like:
SELECT name, COUNT(*) / 2 AS difference_5
FROM (
SELECT a.name name, ABS(a.value - b.value)
FROM tbl a JOIN tbl b USING(name)
WHERE ABS(a.value - b.value) BETWEEN 1 AND 5
) AS t GROUP BY name
edited as per Andreas' comment.
Assuming that each name -> value pair is unique, this will get you the count of times the value is within 5 per name:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON a.name = b.name AND
a.value <> b.value AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
As you'll notice, we also have to exclude the pairs that are equal to themselves.
But if you wanted to count the number of times each name's values came within 5 of any value in the table, you can use:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON NOT (a.name = b.name AND a.value = b.value) AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
See the SQLFiddle Demo for both solutions.
Because the OP also wants de zero counts, we'll need a self- left join. Extra logic is needed if one person has two exactly the same values, these should also be counted only once.
WITH cnts AS (
WITH pair AS (
SELECT t1.zname,t1.zvalue
FROM ztable t1
JOIN ztable t2
ON t1.zname = t2.zname
WHERE ( t1.zvalue < t2.zvalue
AND t1.zvalue >= t2.zvalue - 5 )
OR (t1.zvalue = t2.zvalue AND t1.ctid < t2.ctid)
)
SELECT DISTINCT zname
, COUNT(*) AS znumber
FROM pair
GROUP BY zname
)
, names AS (
SELECT distinct zname AS zname
FROM ztable
GROUP BY zname
)
SELECT n.zname
, COALESCE(c.znumber,0) AS znumber
FROM names n
LEFT JOIN cnts c ON n.zname = c.zname
;
RESULT:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 14
zname | znumber
-------+---------
Sarah | 3
Don | 1
Becca | 0
James | 0
(4 rows)
NOTE: sorry for the CTE, I had not seen th mysql tag,I just liked the problem ;-)
SELECT
A.Name,
SUM(CASE WHEN (A.Value < B.Value) AND (A.Value >= B.Value - 5) THEN 1 ELSE 0 END) Difference_5
FROM
tbl A INNER JOIN
tbl B USING(Name)
GROUP BY
A.Name