Reputation: 319
I have two queries, shown below. The queries select same tables, but have different conditions and columns count. I can't solve how to combine these; is there any way?
The first query is:
SELECT b.centerBranchNumber,
b.countryCode,
a.type,
SUM(DECODE(a.status, 1, 1, 0)) filled
FROM tableA a,
tableB b
WHERE a.branchCode = b.branchCode
AND a.registerNumber IN (SELECT c.registerNumber
FROM tableC c
WHERE c.registerDate <= '01.02.2013'
AND c.state = 'A'
AND c.contractState != 2)
GROUP BY b.centerBranchNumber,
b.countryCode,
a.type;
The results look like this:
BranchNumber CountryCode Type Filled
1452 USA 0 5
1452 USA 2 8
1452 USA 1 16
7854 GER 0 10
7854 GER 1 3
The second query is:
SELECT b.centerBranchNumber,
b.countryCode,
a.type,
SUM(DECODE(a.status, 0, 1, 0)) free,
SUM(DECODE(a.status, 2, 1, 0)) damaged
FROM tableA a,
tableB b
WHERE a.kks_kayitdrm = 'A'
AND a.branchCode = b.branchCode
AND a.recordDate <= '01.02.2013'
GROUP BY b.centerBranchNumber,
a.type,
b.countryCode;
and the result is:
BranchNumber CountryCode Type Free Damage
1452 USA 0 5 2
1452 USA 2 8 1
1452 USA 1 16 5
7854 GER 0 10 9
7854 GER 1 3 16
If I combined, I would want results as follows:
BranchNumber CountryCode Type Filled Free Damage
1452 USA 0 1 5 2
1452 USA 2 2 8 1
1452 USA 1 65 16 5
7854 GER 0 7 10 9
7854 GER 1 45 3 16
Upvotes: 1
Views: 218
Reputation: 52913
Your first query can be re-written as follows:
SELECT b.centerBranchNumber,
b.countryCode,
a.type,
SUM(DECODE(a.status, 1, 1, 0)) filled
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
JOIN tableC
ON a.registerNumber = c.registerNumber
WHERE c.registerDate <= '01.02.2013'
AND c.state = 'A'
AND c.contractState != 2
GROUP BY b.centerBranchNumber,
b.countryCode,
a.type
Turning your second into the ANSI syntax as well (it'll help with more complicated queries as it's extremely obvious when you've done something wrong) it becomes this:
SELECT b.centerBranchNumber,
b.countryCode,
a.type,
SUM(DECODE(a.status, 0, 1, 0)) free,
SUM(DECODE(a.status, 2, 1, 0)) damaged
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
WHERE a.kks_kayitdrm = 'A'
AND a.recordDate <= '01.02.2013'
GROUP BY b.centerBranchNumber,
a.type,
b.countryCode
As you can see there's a large amount of similarity between the two queries. In order to get the result set as you want it there are two ways of doing it. Move the non-similar conditions into a CASE statement in your SUM. Alternatively, you can UNION ALL the two queries together and sum again. I would prefer to use the CASE statement as you reduce the amount of work that you have to do; the downside is that the query becomes less easy to read.
The common part of your query is:
SELECT b.centerBranchNumber,
b.countryCode,
a.type
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
GROUP BY b.centerBranchNumber,
a.type,
b.countryCode
Taking this as the base you can slowly add to it until you get the same result. You have to remember to add the same conditions to the WHERE / JOINS to ensure that you get the same result.
If we first add all your conditions; you have to change the JOIN on tableC in the first query to a LEFT OUTER JOIN. This is exactly the same as the WHERE clause will restrict the result set as if it were a INNER JOIN.
SELECT b.centerBranchNumber,
b.countryCode,
a.type
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
LEFT OUTER JOIN tableC
ON a.registerNumber = c.registerNumber
WHERE c.registerDate <= '01.02.2013'
AND c.state = 'A'
AND c.contractState != 2
GROUP BY b.centerBranchNumber,
b.countryCode,
a.type
Adding the conditions on the second query you now need an OR in your WHERE clause:
SELECT b.centerBranchNumber,
b.countryCode,
a.type
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
LEFT OUTER JOIN tableC
ON a.registerNumber = c.registerNumber
WHERE ( c.registerDate <= '01.02.2013'
AND c.state = 'A'
AND c.contractState != 2
)
OR ( a.kks_kayitdrm = 'A'
AND a.recordDate <= '01.02.2013'
)
GROUP BY b.centerBranchNumber,
b.countryCode,
a.type
Next, your dates seem to be more than a little strange. You don't seem to be doing any conversion of the string literal '01.02.2013'
to a date and as it's not in the form YYYYMMDD your comparisons will not work. You're relying on your NLS_DATE_FORMAT being of the form DD.MM.YYYY
, which you cannot guarantee for every session. To compare a date you should either explicitly convert using the built-in TO_DATE()
function, and the appropriate format model, or an ANSI datetime literal, as I explain more fully in this answer.
Suffice it to say that Oracle explicitly recommends against using implicit conversion because:
SQL statements are easier to understand when you use explicit data type conversion functions.
Implicit data type conversion can have a negative impact on performance, especially if the data type of a column value is converted to that of a constant rather than the other way around.
Implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT parameter.
Algorithms for implicit conversion are subject to change across software releases and among Oracle products. Behavior of explicit conversions is more predictable.
To compare your dates I would therefore recommend using TO_DATE(), for instance:
WHERE ( c.registerDate <= to_date('01.02.2013', 'dd.mm.yyyy')
Lastly we can add the SUMs in; this means repeating your previous conditions within them. You can simplify this for your first query as whether something is "filled" is solely dependent on the LEFT OUTER JOIN, which means that if the registerNumber
of that table, in the JOIN, is not null then the conditions are true.
In both cases I use a CASE statement as opposed to a DECODE; this is because CASE is a lot more powerful, and enforces the correct data-types, which is not guaranteed with DECODE.
Putting all this together your final query becomes:
SELECT b.centerBranchNumber
, b.countryCode
, a.type
, sum(case when a.status = 1 and c.registernumber is not null then 1
else 0
end) as filled
, sum(case when a.status = 0
and a.kks_kayitdrm = 'A'
and a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy')
then 1
else 0
end) as free
, sum(case when a.status = 2
and a.kks_kayitdrm = 'A'
and a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy')
then 1
else 0
end) as damaged
FROM tableA a
JOIN tableB b
ON a.branchCode = b.branchCode
LEFT OUTER JOIN tableC
ON a.registerNumber = c.registerNumber
WHERE ( c.registerDate <= to_date('01.02.2013', 'dd.mm.yyyy')
AND c.state = 'A'
AND c.contractState != 2
)
OR ( a.kks_kayitdrm = 'A'
AND a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy')
)
GROUP BY b.centerBranchNumber
, b.countryCode
, a.type
You can use this approach more generically to combine queries, where it is appropriate.
Upvotes: 1