How to change Query output - plsqldeveloper

I have the down query that output the down results using union, and I want to reformat the output to the next format.
SELECT TO_CHAR(F.DATE, 'YYYY') YEAR,
F.FIELD,
ROUND((ROUND(SUM(F.INJECTION_ VOLUME) / 120, 0) / 1000), 2) INJECTION
FROM INJECTION F
GROUP BY TO_CHAR(F.DATE, 'YYYY'),
F.F_CD
HAVING TO_CHAR(F.DATE, 'YYYY') = '2013'
UNION
SELECT TO_CHAR(S.DATE, 'YYYY') YEAR,
S.FIELD,
ROUND((ROUND(SUM(S.PROD_VOLUME) / 120, 0) / 1000), 2) PRODUCTION
FROM PRODUCTION P
GROUP BY TO_CHAR(S.DATE, 'YYYY'),
S.FIELD
HAVING TO_CHAR(S.DATE, 'YYYY') = '2013'
ORDER BY 1
OUTPUT
YEAR FIELD INJECTION
2013 FARM 122.35
2013 FARM 123.73
2013 DARM 59.99
DESIRED OUTPUT
YEAR FIELD PRODUCTION INJECTION
2013 FARM 122.35 123.75
2013 DARM 59.99

Try this
SELECT T.Year,T.FIELD,T.INJECTION,S.PRODUCTION
From
(
SELECT TO_CHAR(F.DATE, 'YYYY') YEAR,
F.FIELD,
ROUND((ROUND(SUM(F.INJECTION_ VOLUME) / 120, 0) / 1000), 2) INJECTION
FROM INJECTION F
GROUP BY TO_CHAR(F.DATE, 'YYYY'),F.F_CD
HAVING TO_CHAR(F.DATE, 'YYYY') = '2013'
) T Join
(
SELECT TO_CHAR(S.DATE, 'YYYY') YEAR,
S.FIELD,
ROUND((ROUND(SUM(S.PROD_VOLUME) / 120, 0) / 1000), 2) PRODUCTION
FROM PRODUCTION P
GROUP BY TO_CHAR(S.DATE, 'YYYY'),S.FIELD
HAVING TO_CHAR(S.DATE, 'YYYY') = '2013'
) T1 ON T.Year = T1.Year And T.FIELD = S.FIELD

There is specific technique to convert columns into rows. You can use that.
I can outline the following principle. You can rewrite a run.
You can add group by clause to outermost select statement if needed.
Above answers work if tables are only two and have specific data.Following is a general technique.
Example:
SELECT YEAR, FIELD,
CASE WHEN TBL = 'PRODUCTION'
THEN INJECTION
END PRODUCTION,
CASE WHEN TBL = 'INJECTION'
THEN INJECTION
END INJECTION
(
SELECT TO_CHAR(F.DATE, 'YYYY') YEAR,
F.FIELD,
ROUND((ROUND(SUM(F.INJECTION_ VOLUME) / 120, 0) / 1000), 2) INJECTION,
'INJECTION' TBL
FROM INJECTION F
GROUP BY TO_CHAR(F.DATE, 'YYYY'),
F.F_CD
HAVING TO_CHAR(F.DATE, 'YYYY') = '2013'
UNION
SELECT TO_CHAR(S.DATE, 'YYYY') YEAR,
S.FIELD,
ROUND((ROUND(SUM(S.PROD_VOLUME) / 120, 0) / 1000), 2) ,
'PRODUCTION' TBL
FROM PRODUCTION P.
GROUP BY TO_CHAR(S.DATE, 'YYYY'),
S.FIELD
HAVING TO_CHAR(S.DATE, 'YYYY') = '2013'
ORDER BY 1
)
I can make suitable modifications in query. Following code gives one more clue.
By writing suitable logic, desired results can be generated.
SELECT YEAR, FIELD,
MAX (
CASE WHEN FIELD = 'FARM'
THEN INJECTION
END
) PRODUCTION,
MAX ( CASE WHEN FIELD <> 'FARM'
THEN INJECTION
END
) INJECTION
(
SELECT TO_CHAR(F.DATE, 'YYYY') YEAR,
F.FIELD,
ROUND((ROUND(SUM(F.INJECTION_ VOLUME) / 120, 0) / 1000), 2) INJECTION
FROM INJECTION F
GROUP BY TO_CHAR(F.DATE, 'YYYY'),
F.F_CD
HAVING TO_CHAR(F.DATE, 'YYYY') = '2013'
UNION
SELECT TO_CHAR(S.DATE, 'YYYY') YEAR,
S.FIELD,
ROUND((ROUND(SUM(S.PROD_VOLUME) / 120, 0) / 1000), 2)
FROM PRODUCTION P.
GROUP BY TO_CHAR(S.DATE, 'YYYY'),
S.FIELD
HAVING TO_CHAR(S.DATE, 'YYYY') = '2013'
ORDER BY 1
)
GROUP BY YEAR, FIELD

I just used your code (with some modifications) as t1 and t2 and joined the two tables so that the results come in rows (instead of unioning together):
select t1.YEAR, t1.FIELD, t2.PRODUCTION, t1.INJECTION
from (
SELECT TO_CHAR(F.DATE, 'YYYY') YEAR, F.FIELD, ROUND((ROUND(SUM(F.INJECTION_ VOLUME) / 120, 0) / 1000), 2) INJECTION
FROM INJECTION F
WHERE TO_CHAR(F.DATE, 'YYYY') = '2013'
GROUP BY TO_CHAR(F.DATE, 'YYYY'),F.F_CD
) as t1
full outer join (
SELECT TO_CHAR(S.DATE, 'YYYY') YEAR, S.FIELD, ROUND((ROUND(SUM(S.PROD_VOLUME) / 120, 0) / 1000), 2) PRODUCTION
FROM PRODUCTION S
WHERE TO_CHAR(S.DATE, 'YYYY') = '2013'
GROUP BY TO_CHAR(S.DATE, 'YYYY'),S.FIELD
) as t2 on t1.YEAR = t2.YEAR and t1.FIELD = t2.FIELD
Note that for the last value, we'll have "NULL" in the result set. To change null to zero (or anything else) you may use isnull(field, 0) in MS SQL Server or ifnull(field, 0) in MySQL.

Related

mysql address outer query from subquery

I have this query:
SELECT
sec_to_time(avg(t1.sessiontime)) as aloc,
CONCAT(TRUNCATE(sum(t1.terminatecauseid = 1) * 100 / count(*),
1),
'%') as asr,
count(*) as calls,
cast(t1.destination as unsigned) as prefix,
t2.destination as destination,
SEC_TO_TIME(sum(t1.sessiontime)) as duration
FROM
cc_call AS t1
inner join
cc_prefix as t2 ON t1.destination = t2.prefix
WHERE
t1.card_id = '133' AND t1.starttime >= ('2014-06-1') AND t1.starttime <= ('2014-07-01 23:59:59') and t1.terminatecauseid = 1
group by t1.destination
order by duration DESC
LIMIT 0 , 25
t1.terminatecauseid = 1 means successful call,
'asr' means average success rate,
Im trying to find out how many calls with (t1.terminatecauseid = 1) from the total calls made to an extension.
this line doesn't work:
sum(t1.terminatecauseid = 1) * 100 / count(*)
since I already have (t1.terminatecauseid = 1) in the WHERE clause.
Im thinking about putting a subquery, to retrieve total calls, where count(*) currently is.
How can I have this query calculate the ASR with total calls made?
example sqlfiddle
if possible, I'd like to not show results with duration=NULL
Use conditional aggregation, something like this:
SELECT sec_to_time(avg(case when t1.terminatecauseid = 1 then t1.sessiontime end)) as aloc,
CONCAT(TRUNCATE(sum(t1.terminatecauseid = 1) * 100 / count(*),
1),
'%') as asr,
count(*) as TotalCalls,
sum(t1.terminatecauseid = 1) as Terminated1Calls,
cast(t1.destination as unsigned) as prefix,
t2.destination as destination,
SEC_TO_TIME(sum(case when t1.terminatecauseid = 1 then t1.sessiontime end)) as duration
FROM cc_call t1 inner join
cc_prefix t2
ON t1.destination = t2.prefix
WHERE t1.card_id = '133' AND
t1.starttime >= ('2014-06-1') AND t1.starttime <= ('2014-07-01 23:59:59')
group by t1.destination
order by duration DESC
LIMIT 0 , 25;

Concatenation using a mysql variable

The problem in my query particularly in this piece:
#a:= concat(#a, ',', B.call_account_id) AS paid_account_id
Here is the whole query:
SELECT operator_id, paid_account_ids, SUM( goods_count * price ) AS sales_volume, count(*) AS sales_cnt
FROM (
SELECT B.operator_id, #a:= concat(#a, ',', B.call_account_id) AS paid_account_ids, B.call_time, A.goods_count, A.price, UNIX_TIMESTAMP( A.completion_date ) AS paid_ts
FROM call_module_data B
INNER JOIN ak_accounts A ON ( A.account_id = B.call_account_id AND A.goods_count >=1 )
WHERE B.call_status IN (1,7) AND A.status_id = 5
AND operator_id IN ( $op_ids )
$and_str_accounts
GROUP BY A.account_id
HAVING call_time < (paid_ts + $time_shift)
) AS T
GROUP BY operator_id";
So the expression mentioned above should produce the concatenated string of account ids (e.g 3341,4355,4433...). But I got NULL instead of desired string.
Please help to resolve. Thanks in advance.
Use GROUP_CONCAT() function instead of String concatenation
Change
#a:= concat(#a, ',', B.call_account_id) AS paid_account_id
above string to
GROUP_CONCAT(B.call_account_id) AS paid_account_ids
Final Answer:
SELECT operator_id, GROUP_CONCAT(paid_account_ids) AS paid_account_ids,
SUM(goods_count * price) AS sales_volume, COUNT(*) AS sales_cnt
FROM (SELECT B.operator_id, GROUP_CONCAT(B.call_account_id) AS paid_account_ids,
B.call_time, A.goods_count, A.price,
UNIX_TIMESTAMP(A.completion_date) AS paid_ts
FROM call_module_data B
INNER JOIN ak_accounts A ON A.account_id = B.call_account_id AND A.goods_count >=1
WHERE B.call_status IN (1,7) AND A.status_id = 5 AND operator_id IN ($op_ids)
$and_str_accounts
GROUP BY A.account_id HAVING call_time < (paid_ts + $time_shift)
) AS T
GROUP BY operator_id;

Optimize SQL Server Query

I have to do a query to get the total cost of previous month and compared to current month to calculate the percentage difference.
this is the script:
create table #calc
(
InvoiceDate Date,
TotalCost decimal (12,2)
)
insert into #calc values ('2013-07-01', 9470.36)
insert into #calc values ('2013-08-01', 11393.81)
and this is the query:
select InvoiceDate,
TotalCost,
PrevTotalCost,
(CASE WHEN (PrevTotalCost = 0)
THEN 0
ELSE (((TotalCost - PrevTotalCost) / PrevTotalCost) * 100.0)
END) AS PercentageDifference
from (
select a.InvoiceDate, a.TotalCost,
isnull((select b.TotalCost
from #calc b
where InvoiceDate = (select MAX(InvoiceDate)
from #calc c
where c.InvoiceDate < a.InvoiceDate)), 0) as PrevTotalCost
from #calc a) subq
Is there a more efficient way to do it for cgetting the previous month?
Using a ranking function to put more burden on sorts than table scans seems the fastest when using no indexes. The query below processed 6575 records in under a second:
SELECT
Main.InvoiceDate,
Main.TotalCost,
PreviousTotalCost=Previous.TotalCost,
PercentageDifference=
CASE WHEN COALESCE(Previous.TotalCost,0) = 0 THEN 0
ELSE (((Main.TotalCost - Previous.TotalCost) / Previous.TotalCost) * 100.00)
END
FROM
(
SELECT
InvoiceDate,
TotalCost,
OrderInGroup=ROW_NUMBER() OVER (ORDER BY InvoiceDate DESC)
FROM
Test
)AS Main
LEFT OUTER JOIN
(
SELECT
InvoiceDate,
TotalCost,
OrderInGroup=ROW_NUMBER() OVER (ORDER BY InvoiceDate DESC)
FROM
Test
)AS Previous ON Previous.OrderInGroup=Main.OrderInGroup+1
Using nested looping as the case when getting the previous invoice cost in a select subquery proves the slowest - 6575 rows in 30 seconds.
SELECT
X.InvoiceDate,
X.TotalCost,
X.PreviousTotalCost,
PercentageDifference=
CASE WHEN COALESCE(X.PreviousTotalCost,0) = 0 THEN 0
ELSE (((X.TotalCost - X.PreviousTotalCost) / X.PreviousTotalCost) * 100.00)
END
FROM
(
SELECT
InvoiceDate,
TotalCost,
PreviousTotalCost=(SELECT TotalCost FROM Test WHERE InvoiceDate=(SELECT MAX(InvoiceDate) FROM Test WHERE InvoiceDate<Main.InvoiceDate))
FROM
Test AS Main
)AS X
Your query processed 6575 records in 20 seconds with the biggest cost coming from the nested loops for inner join
select InvoiceDate,
TotalCost,
PrevTotalCost,
(CASE WHEN (PrevTotalCost = 0)
THEN 0
ELSE (((TotalCost - PrevTotalCost) / PrevTotalCost) * 100.0)
END) AS PercentageDifference
from (
select a.InvoiceDate, a.TotalCost,
isnull((select b.TotalCost
from Test b
where InvoiceDate = (select MAX(InvoiceDate)
from #calc c
where c.InvoiceDate < a.InvoiceDate)), 0) as PrevTotalCost
from Test a) subq
Using indexes would be a big plus unless you are required to use temp tables.
Hope this helps :)
SELECT
`current`.`InvoiceDate`,
`current`.`TotalCost`,
`prev`.`TotalCost` AS `PrevTotalCost`,
(`current`.`TotalCost` - `prev`.`TotalCost`) AS `CostDifference`
FROM dates `current`
LEFT JOIN
dates `prev`
ON `prev`.`InvoiceDate` <= DATE_FORMAT(`current`.`InvoiceDate` - INTERVAL 1 MONTH, '%Y-%m-01');
Screenshot of the results I got: http://cl.ly/image/0b3z2x1f2H1n
I think this might be what you're looking for.
Edit: I wrote this query in MySQL, so it's possible you may need to alter a couple minor syntax things for your server.

COUNT(DISTINCT) in multiple columns in SQL Server 2008

In Oracle, it's possible to get a count of distinct values in multiple columns by using the || operator (according to this forum post, anyway):
SELECT COUNT(DISTINCT ColumnA || ColumnB) FROM MyTable
Is there a way to do this in SQL Server 2008? I'm trying to perform a single query to return some group statistics, but I can't seem to do it.
For example, here is a table of values I'm trying to query:
AssetId MyId TheirId InStock
328 10 10 1
328 20 20 0
328 30 30 0
328 40 10 0
328 10 10 0
328 10 10 0
328 10 10 0
328 10 10 0
For AssetId #328, I want to compute the total number of unique IDs in the MyId and TheirId columns (4 = 10, 20, 30, 40), as well as the total number of non-zero rows in the InStock column (1):
AssetId TotalIds AvailableIds
328 4 1
Is there a way to work this magic somehow?
You can use a cross apply and values.
select T1.AssetId,
count(distinct T2.ID) TotalIds,
sum(case T2.InStock when 0 then 0 else 1 end) AvailableIds
from YourTable as T1
cross apply(values(T1.MyId, T1.InStock),
(T1.TheirId, 0)
) as T2(ID, InStock)
group by T1.AssetId
SE-Data
Or you can do a union all in a sub query.
select T.AssetId,
count(distinct T.ID) TotalIds,
sum(case T.InStock when 0 then 0 else 1 end) AvailableIds
from (
select AssetId, MyId as ID, InStock
from YourTable
union all
select AssetID, TheirId, 0
from YourTable
) as T
group by T.AssetId
I think it's good solution for you
SELECT COUNT(*)
FROM (SELECT DISTINCT Column1, Column2
FROM MyTable) A
You can get the result like this:
DECLARE #t TABLE (AssetId INT, MyId INT, TheirId INT, InStock INT)
INSERT #t
VALUES
(328,10, 10, 1)
,(328,20, 20, 0)
,(328,30, 30, 0)
,(328,40, 10, 0)
,(328,10, 10, 0)
,(328,10, 10, 0)
,(328,10, 10, 0)
,(328,10, 10, 0)
;WITH a AS(
SELECT AssetId,
COUNT(col) cnt
FROM
(
SELECT MyId col, AssetId
FROM #t
UNION
SELECT TheirId col, AssetId
FROM #t
) b
GROUP BY AssetId
)
SELECT a.AssetId,
a.cnt TotalIds,
SUM(CASE WHEN InStock <> 0 THEN 1 ELSE 0 END) AvailableIds
FROM #t c
JOIN a ON a.AssetId = c.AssetId
GROUP BY a.AssetId, a.cnt
In Common Table Expression (WITH code block) 'uniqueness' is guaranteed by using UNION operator which discards duplicate values, that's why COUNT(col) doesn't need to be used like COUNT(DISTINCT col).
You can follow the Oracle example and concatenate the values together (that is what the Oracle query is doing). You just have to convert the values to characters first:
select AssetId,
count(distinct cast(MyId as varchar(8000))+','+cast(TheirId as varchar(8000)
) totalIds,
count(distinct case when inStock> 0
then cast(MyId as varchar(8000))+','+cast(TheirId as varchar(8000)
end) as AvailableIds
from t
group by Assetid
You can also do it as a subquery:
select AssetId, count(*) as TotalIds,
sum(case when inStock > 0 then 1 else 0 end) as AvailableIds
from (select AssetId, myId, theirId, max(inStock) as inStock
from t
group by AssetId, myId, theirId
) a
group by AssetId
"Theoretically", I like the second approach better, since it is more set-based. However, if you find yourself trying to count distinct combinations columns in several different variables, the string concatenation approach is more practical.
If you don't like to use CTE's, you can try using following solution. The gist of it is to
select the TotalID's for each AssetID in a seperate subquery
select the AvailableIDs for each AssetID in a seperate subquery
JOIN the results of both subqueries to produce the final results.
The statement as is works on the entire table. You can get the results for a single AssetID by adding an appropriate where clause to the entire group.
SQL Statement
SELECT a.AssetId, t.TotalIDs, a.AvailableIDs
FROM (
SELECT AssetID, TotalIDs = COUNT(*)
FROM (
SELECT AssetID
FROM MyTable
GROUP BY
MyId, TheirID, AssetID
) t
GROUP BY
AssetID
) AS t
INNER JOIN (
SELECT AssetID, AvailableIDs = SUM(InStock)
FROM MyTable
GROUP BY
AssetID
) AS a ON a.AssetId = t.AssetId
Test script
;WITH MyTable (AssetId, MyId, TheirId, InStock) AS (
SELECT * FROM (VALUES
(328, 10, 10, 1)
, (328, 20, 20, 0)
, (328, 30, 30, 0)
, (328, 40, 10, 0)
, (328, 10, 10, 0)
, (328, 10, 10, 0)
, (328, 10, 10, 0)
, (328, 10, 10, 0)
, (329, 10, 10, 0)
, (329, 10, 20, 1)
) AS a (b, c, d, e)
)
SELECT a.AssetId, t.TotalIDs, a.AvailableIDs
FROM (
SELECT AssetID, TotalIDs = COUNT(*)
FROM (
SELECT AssetID
FROM MyTable
GROUP BY
MyId, TheirID, AssetID
) t
GROUP BY
AssetID
) AS t
INNER JOIN (
SELECT AssetID, AvailableIDs = SUM(InStock)
FROM MyTable
GROUP BY
AssetID
) AS a ON a.AssetId = t.AssetId
Option #1
select
AssetID, count(distinct MyId) As MyId, SUM(InStock) InStock
From T
Group By
AssetID
Option #2 Without CTE
Select AssetID, count(MyId), sum(InStock) InStock From
(
select
AssetID, MyId, SUM(InStock) InStock
From MyTable
Group By
AssetID, MyId
)K
Group by AssetID
Option #3 With CTE
;With Sub(AssetID, MyId, InStock)
As
(
select
AssetID, MyId, SUM(InStock) InStock
From MyTable
Group By
AssetID, MyId
)
Select AssetID, count(MyId), sum(InStock) From
(
Select * from Sub
)K

Percentages in MySQL

Using this query,
SELECT company, YEAR( date ) as year, COUNT( * ) as total
FROM table
WHERE company = "Medtronic"
OR company = "Private"
GROUP BY YEAR( date )
I get a table like this:
Company year total
Medtronic 1998 6
Private 1998 5
Medtronic 1999 5
Private 1999 1
How do I calculate the % that is contributed by each company for each year?
For example, the percentage contributed by Medtronic in year 1998 is
6 / (6+5) = 54.5%
I have been trying to make a MySQL query to calculate the percentages.
Thanks guys.
Use:
SELECT x.company,
x.year,
x.annual_total
x.annual_total / y.total AS percentage
FROM (SELECT t.company,
YEAR(t.date) as year,
COUNT( * ) as annual_total
FROM TABLE t
WHERE t.company IN ('Medtronic', 'Private')
GROUP BY YEAR( t.date ) ) x
JOIN (SELECT t.company,
COUNT(*) 'total'
FROM TABLE t
WHERE t.company IN ('Medtronic', 'Private')
GROUP BY t.company) y ON y.company = x.company
If you want the percentage with particular decimal places, use:
CAST(x.annual_total / y.total AS DECIMAL(2,2)) AS percentage
Check that this gives the count per company you expect:
SELECT t.company,
COUNT(*) 'total'
FROM TABLE t
WHERE t.company IN ('Medtronic', 'Private')
GROUP BY t.company
My SQL query:
SELECT x.company, x.year, x.annual_total, CAST( x.annual_total / y.total AS DECIMAL( 2, 2 ) ) AS percentage
FROM (
SELECT t.company, YEAR( t.date ) AS year, COUNT( * ) AS annual_total
FROM my_patents AS t
WHERE t.company = 'Private'
GROUP BY YEAR( t.date )
)x
JOIN (
SELECT t.company, COUNT( * ) AS total
FROM my_patents AS t
WHERE t.company = 'Medtronic'
OR t.company = 'Private'
GROUP BY t.company
)y ON y.company = x.company
my results:
Private 1998 5 0.04
when i run this query:
SELECT t.company, YEAR( date ) , COUNT( * ) AS total
FROM my_patents AS t
WHERE t.company = 'Medtronic'
OR t.company = 'Private'
GROUP BY t.company, YEAR( date )
I get
Medtronic 1998 6
Private 1998 5