let me preface that I've done a good bit of searching to see if this issue can be resolved but have yet to find an answer that relates, so I'll ask now. I have a query that works (mostly) to pull from two separate tables to show a total amount of pc models in each of four locations(these locations are in a single column, so I'm trying to select them as their own columns through aliases). The query pulls all required info, but not as I was hoping it would.
Expectation:
model | loc1 | loc2 | loc3 | loc4
5530 | 3 | 2 | 1 | 0
6440 | 10 | 0 | 3 | 20
Reality:
model | loc1 | loc2 | loc3 | loc4
5530 | 3 | null | null | null
5530 | null | 2 | null | null
5530 | null | null | 1 | null
etc..
Here is my query, any help to make the reality match the expectation if possible would be appreciated.
SELECT
a.model,
(select COUNT(a.model)
WHERE a.location = 'AoC-Reno') AS Reno,
(select count(a.model)
where a.location = 'AoC-Fargo') AS Fargo,
(select count(a.model)
where a.location = 'EoC') AS EoC,
(select count(a.model)
where a.location = 'APoC') as APoC
FROM assets AS a
join models m on m.model = a.model
WHERE m.type IN ('Desktop','Laptop')
AND a.model = m.model
AND a.status != 'Recycled'
GROUP BY m.model, a.location
ORDER BY m.model
Use conditional aggregation:
SELECT a.model,
SUM(a.location = 'AoC-Reno') AS Reno,
SUM(a.location = 'AoC-Fargo') AS Fargo,
SUM(a.location = 'EoC') AS EoC,
SUM(a.location = 'APoC') as APoC
FROM models m JOIN
assets a
on m.model = a.model
WHERE m.type IN ('Desktop', 'Laptop') and a.status <> 'Recycled'
GROUP BY m.model
ORDER BY m.model;
Your query has multiple issues:
You are using a "subquery", but there is no FROM clause.
You are aggregating by both model and location, but you want only one row per model.
The JOIN condition is both in the ON clause and repeated in the WHERE clause.
You can do a conditional aggregation (count)
SELECT a.model,
SUM(CASE WHEN a.location = 'AoC-Reno' THEN 1 END) reno,
SUM(CASE WHEN a.location = 'AoC-Fargo' THEN 1 END) farggo,
SUM(CASE WHEN a.location = 'EoC' THEN 1 END) eoc,
SUM(CASE WHEN a.location = 'APoC' THEN 1 END) apoc
FROM assets AS a JOIN models m
ON m.model = a.model
WHERE m.type IN ('Desktop','Laptop')
AND a.status != 'Recycled'
GROUP BY m.model
ORDER BY m.model
Related
I am trying to get a table with stage_name and its count in respective loan product. Like in below example stage_name is RCO and there are three loan product, Auto loan, Consumer loan and Credit card. Though I have used the logic and getting the right output, but in the output, I am getting the separate row for each stage_name and loan product case. I want only one row with all the three result. Please look at my code below, actual output and desired output:
SELECT
'RCO',
CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Consumer_Loan,
CASE
WHEN sq2.loan_type = 'Auto Loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Auto_Loan,
CASE
WHEN sq2.loan_type = 'Credit Card'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Credit_Card
FROM
(SELECT
'RC0' AS ws_name, 'Consumer loan' AS loan_type,
COUNT(DISTINCT a.bpm_referenceno) AS user_count,
a.takenby AS user_id
FROM
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM
BM_RLOS_EXTTABLE m
WHERE
m.loan_type = 'Consumer Loan') sq1 ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE
a.winame='RCO'
GROUP BY
a.takenby
UNION
SELECT 'RC0','Auto loan',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Auto Loan')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby
UNION
SELECT 'RC0','Credit Card',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Credit Card')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby) sq2
GROUP BY sq2.ws_name,sq2.loan_type
Actual output:
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | NULL | NULL | 8 |
|--------------|-------------|-------------|-------------|
| RCO | NULL | 55 | NULL |
|--------------|-------------|-------------|-------------|
| RCO | 81 | NULL | NULL |
|--------------|-------------|-------------|-------------|
Required Output
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | 81 | 55 | 8 |
|--------------|-------------|-------------|-------------|
The top half should be like this - reverse the usage of SUM and CASE, and remove the last GROUP BY altogether
SELECT
'RCO',
SUM(CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN sq2.user_count
ELSE 0 END
)
AS Consumer_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Auto loan'
THEN sq2.user_count
ELSE 0 END
)
AS Auto_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Credit Card'
THEN sq2.user_count
ELSE 0 END
)
AS Credit_Card
FROM
<your existing query, with the final GROUP BY removed>
But you need to remove the GROUP BY from the end altogether
I have this table register:
id quantity type
1 | 10 | in
2 | 5 | in
1 | 3 | out
1 | 2 | out
2 | 5 | out
3 | 2 | in
3 | 1 | out
I want the balance of each stock *sum of type='in' - sum of type= 'out'*.
Desired output would be:
1 | 5
2 | 0
3 | 1
I also have another table item:
id | name
1 | A
2 | B
3 | C
Is it possible to view the output with the item name instead of the id?
So the final result is like:
A | 5
B | 0
C | 1
The basic idea is conditional aggregation --case inside of sum(). You also need a join to get the name:
select i.name,
sum(case when r.type = 'in' then quantity
when r.type = 'out' then - quantity
else 0
end) as balance
from register r join
item i
on r.id = i.id
group by i.name;
Acccording to description as mentioned in above question,as a solution to it please try executing following SQL query
SELECT i.name,
#in_total:= (select sum(quantity) from register where type = 'in'
and id = r.id group by id),
#out_total:= (select sum(quantity) from register where type = 'out'
and id = r.id group by id),
#balance:= (#in_total - #out_total) as balance
FROM `register`
as r join item i on r.id = i.id group by r.id
CROSS JOIN (SELECT #in_total := 0,
#out_total := 0,
#balance := 0) AS user_init_vars
I have a normalized sql-database with several tables and an html-form to read out the data. There is one table that contains product-characteristics and any product can have one or more characteristics.
Main table: product_main pm,
Table with characteristics: product_characteristics pc,
product_id is the foreign key.
id | product_id | product_name | characteristic
1 | 27 | Product_C | characteristic5
2 | 27 | Product_C | characteristic11
3 | 27 | Product_C | characteristic3
4 | 27 | Product_C | characteristic47
5 | 27 | Product_C | characteristic34
6 | 28 | Product_D | characteristic29
7 | 28 | Product_D | characteristic63
8 | 28 | Product_D | characteristic2
Now, I want to read out all product_names that match the selected criteria. This works fine, when only one characteristic is selected:
SELECT pm.name
FROM product_main pm, product_characteristics pc
WHERE pc.characteristic = 'characteristic47'
AND pm.id = pc.product_id
The correct result is: Product_C.
If more than one characteristics are selected simultaneously, e.g.:
SELECT pm.name
FROM product_main pm, product_characteristics pc
WHERE pc.characteristic = 'characteristic47'
AND pc.characteristic = 'characteristic11'
AND pm.id = pc.product_id
there is no result, although Product_C matches both criteria.
Could someone please tell me, how I can solve this problem?
One option uses conditional aggregation:
SELECT product_id, product_name
FROM product_main
GROUP BY product_id, product_name
HAVING SUM(CASE WHEN characteristic = 'characteristic47' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN characteristic = 'characteristic11' THEN 1 ELSE 0 END) > 0
If you also want to retain all original records in product_main which have both matching characteristics, then you can join the above table back to your original table:
SELECT t1.*
FROM product_main t1
INNER JOIN
(
SELECT product_id
FROM product_main
GROUP BY product_id
HAVING SUM(CASE WHEN characteristic = 'characteristic47' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN characteristic = 'characteristic11' THEN 1 ELSE 0 END) > 0
) t2
ON t1.product_id = t2.product_id
Well pc.characteristic cannot be both characteristic47 AND characteristic11 at the same time, but it could be (characteristic47 OR characteristic11)
So amend the query like so
SELECT pm.name
FROM product_main pm, product_characteristics pc
WHERE ( pc.characteristic = 'characteristic47' OR
pc.characteristic = 'characteristic11' )
AND pm.id = pc.product_id
You sould also be using JOIN's like so
SELECT pm.name
FROM product_main pm
JOIN product_characteristics pc ON pm.id = pc.product_id
WHERE ( pc.characteristic = 'characteristic47' OR
pc.characteristic = 'characteristic11' )
I have 2 tables :-
Table T
ID | val
1 | abcd
2 | 1234
3 | asd
4 | lkj
And another table M
ID | T_ID | Type
1 | 1 | I
2 | 1 | S
3 | 2 | I
4 | 2 | I
5 | 3 | I
6 | 4 | S
I want to write a query that joins table T and M on m.T_ID = T.ID but it should not return T.ID if any M mapped to it has Type S i.e. the above set of data should return values T.ID = 2,3 and not 1,4 because M mapped to it has Type S
One way to do it would be to write a inner query. Something like :-
SELECT T.id
FROM table1 T
JOIN table2 M
ON M.t_id = T.id
WHERE T.id NOT IN (SELECT m2.t_id
FROM table2 m2
WHERE m2.type = 'S')
But inner query can be very expensive as my table M has millions of rows. Is there a better way to do this ?
Use a conditional COUNT
SELECT T.id
FROM table1 T
JOIN table2 M
ON M.t_id = T.id
GROUP BY T.id
HAVING COUNT( CASE WHEN M.Type = 'S' THEN 1 END ) = 0
Mean you dont have 'S' in that group.
Not the prettiest but it seems to work
select T.ID
from Table1 T
left join Table2 M on M.T_ID = T.ID
group by T.Id
having sum(case when M.Type = 'S' then 1 else 0 end) = 0
You should check if it is actually less expensive in the execution plan.
You should look into LEFT JOIN as opposed to INNER JOIN.
Using MySQL and I have two tables similar to the following. Both actually have more rows and more columns but for ease of reading, I've only put a few
wp_ahm_files
ID | Field_A | Field_B | Field_C
--------------------------------
69 | ABC | DEF | GHI
wp_ahm_filemeta
pID in this table refers to the ID of the table above
ID | pID | Name | Value
---------------------------------
25 | 69 | Version | 12345
26 | 69 | Expiry | 29/08/1981
How do I bring back a resultset such as
ID | Field_A | Field_B | Field_C | Version | Expiry
-------------------------------------------------------
69 | ABC | DEF | GHI | 12345 | 29/08/1981
You should be able to JOIN to your wp_ahm_filemeta twice to get the result:
select f.id,
f.field_a,
f.field_b,
f.field_c,
m1.value version,
m2.value expiry
from wp_ahm_files f
left join wp_ahm_filemeta m1
on f.id = m1.pid
and m1.name = 'Version'
left join wp_ahm_filemeta m2
on f.id = m2.pid
and m2.name = 'Expiry';
See SQL Fiddle with Demo. The key is to place a filter on the JOIN condition to return the rows with the specific name value you want.
You could also convert the rows of data into columns by using an aggregate function with a CASE expression:
select f.id,
f.field_a,
f.field_b,
f.field_c,
max(case when m.name = 'Version' then m.value end) version,
max(case when m.name = 'Expiry' then m.value end) expiry
from wp_ahm_files f
left join wp_ahm_filemeta m
on f.id = m.pid
group by f.id, f.field_a, f.field_b, f.field_c;
See SQL Fiddle with Demo
Something like
select a.*, version.value version, expiry.value expiry
from a join
( select * from b where name = 'Version' ) version
on version.table_a_id = a.id
join
( select * from b where name = 'Expiry' ) expiry
on expiry.table_a_id = a.id