SELECT totals from previous SELECT using SQL*Plus - sql

Using SQL*Plus to generate a listing that is e-mailed to a customer, e.g.:
SET MARKUP HTML ON
SPOOL spool.html
SELECT order_number, entry_date, delivery_date, customer_order_number, order_totals_quantity, TRUNC(order_totals_sqm,2), order_totals_net_value FROM orders WHERE entry_date = SYSDATE;
How can I also create a row that shows the total of the listed order_totals fields and keep them in line with those fields?
i.e. if I did a separate SELECT COUNT() for those fields it would list them under the first 3 when really they need to be underneath the original SELECT.
Update: This is what I'm looking for, if it's possible.
other columns ... order_totals_quantity | TRUNC(order_totals_sqm,2) | order_totals_net_value
--------------------------------------------------------------------------------------------
Total | Total | Total

Maybe...
Depends on what aggregate you're wanting and what denotes a unique record so as not to sum quantities incorrectly.
SELECT order_number, entry_date, delivery_date, customer_order_number,
sum(order_totals_quantity), sum(TRUNC(order_totals_sqm,2)), sum(order_totals_net_value)
FROM orders
WHERE entry_date = SYSDATE;
GROUP BY GROUPING SETS ((order_number, entry_date, delivery_date, customer_order_number),
())
Example found: http://www.oracle-base.com/articles/misc/rollup-cube-grouping-functions-and-grouping-sets.php

Try this [assuming you are using oracle]:
SELECT order_number, entry_date, delivery_date, customer_order_number, order_totals_quantity, TRUNC(order_totals_sqm,2), order_totals_net_value,tot.a, tot.b
FROM orders, (select sum(order_totals_quantity) a, sum(order_totals_net_value ) b from orders WHERE entry_date = SYSDATE) tot
WHERE entry_date = SYSDATE;

As you are using SQL*Plus, there is an easier method using computes. This has the advantage of no extra SQL running on the server. Here is an example you can adapt for your query:
BREAK ON report
COMPUTE SUM LABEL total OF a ON report
SELECT 1 AS a FROM dual UNION ALL
SELECT 2 AS a FROM dual UNION ALL
SELECT 3 AS a FROM dual;
A
-------------
1
2
3
-------------
6
3 rows selected.
You can use other aggregates as well. Here is a link to the full documentation: COMPUTE.

Related

oracle sql counts

If portfolio id has the stock code using the count.
example Portfolio id 500 has stock_code 'IBM' It count one Portfolio id 500 has stock_code 'GBA' it counts two
If input Portfolio id 500 and stock_code 'ABC' It count 0
not raising an error message because of the stock_code is exist.
Portfolio id stock_code
500 IBM
500 GBA
500 GBA
select count(distinct stock_code,portfolio_number)
into V_count
from Stock
where P_Stock_Code = stock_code
and P_Portfolio_number=portfolio_number;
showing error
invalid number of arguments
pass two INPUT parameters
or maybe I can not verify the id own or not own the stock_code using count??
As you can already understand you can not pass two parameters to COUNT function. But You can do something like below instead.
WITH STOCK
AS (SELECT 500 PORTFOLIO_ID, 'IBM' AS STOCK_CODE FROM DUAL
UNION ALL
SELECT 500 PORTFOLIO_ID, 'GBA' AS STOCK_CODE FROM DUAL
UNION ALL
SELECT 500 PORTFOLIO_ID, 'GBA' AS STOCK_CODE FROM DUAL)
SELECT CASE WHEN CNT = 0 THEN 'CASE1' WHEN CNT >= 1 THEN 'CASE2' END KEYZ
FROM ( SELECT STOCK_CODE, COUNT (PORTFOLIO_ID) AS CNT
FROM STOCK
GROUP BY STOCK_CODE)
I think you are just trying to check if Passed P_Portfolio_number has how many distinct P_Stock_Code.
You can use the following:
select count(distinct stock_code)
into V_count -- 0,1,2 .. you can use it wherever you want then
from Stock
where P_Stock_Code = stock_code
and P_Portfolio_number=portfolio_number;
Cheers!!
You can use the below
select count(1)
into V_count
from (select distinct stock_code, portfolio_number
from Stock
where P_Stock_Code = stock_code
and P_Portfolio_number = portfolio_number);
Oracle does not support a list of columns in COUNT DISTINCT. I wrote an according request in Oracle's database ideas portal more than two years ago: https://community.oracle.com/ideas/18664. You can register and then upvote this idea. I think, however, that for the given situation you don't need it.
Just use COUNT(*). The following counts the rows for a given combination of stock code and portfolio number (1 for 500|IBM, 2 for 500|GBA, 0 for 500|ABC).
In PL/SQL:
create or replace procedure myproc(p_stock_code varchar2, p_portfolio_number integer) as
v_count integer;
begin
select count(*)
into v_count
from stock
where stock_code = p_stock_code
and portfolio_number = p_portfolio_number;
dbms_output.put_line(v_count);
end myproc;
In SQL:
select count(*)
from stock
where stock_code = :p_stock_code
and portfolio_number = :p_portfolio_number;

SQL - When result is duplicated on 2 fields remove all

When i run this query
SELECT
DT.CONTRACT_NUMBER,
DT.ROLE,
DT.TAX_ID,
DT.EFFECTIVE_DATE
FROM DATA_TABLE DT
I get this result.
Id like to remove results where the TAX ID appears more than once for each contract.
i.e This result would be gone. If they had 3 results they would be gone.
I think window functions might be the way to go:
SELECT DT.CONTRACT_NUMBER, DT.ROLE, DT.TAX_ID, DT.EFFECTIVE_DATE
FROM (SELECT DT.CONTRACT_NUMBER, DT.ROLE, DT.TAX_ID, DT.EFFECTIVE_DATE,
COUNT(*) OVER (PARTITION BY TAX_ID) as cnt
FROM DATA_TABLE DT
WHERE DT.CONTRACT_NUMBER = '551000280'
) DT
WHERE CNT = 1;
If you actually want to keep one row per tax id, then use row_number() instead of count(*).

Grouping records on consecutive dates

If I have following table in Postgres:
order_dtls
Order_id Order_date Customer_name
-------------------------------------
1 11/09/17 Xyz
2 15/09/17 Lmn
3 12/09/17 Xyz
4 18/09/17 Abc
5 15/09/17 Xyz
6 25/09/17 Lmn
7 19/09/17 Abc
I want to retrieve such customer who has placed orders on 2 consecutive days.
In above case Xyz and Abc customers should be returned by query as result.
There are many ways to do this. Use an EXISTS semi-join followed by DISTINCT or GROUP BY, should be among the fastest.
Postgres syntax:
SELECT DISTINCT customer_name
FROM order_dtls o
WHERE EXISTS (
SELEST 1 FROM order_dtls
WHERE customer_name = o.customer_name
AND order_date = o.order_date + 1 -- simple syntax for data type "date" in Postgres!
);
If the table is big, be sure to have an index on (customer_name, order_date) to make it fast - index items in this order.
To clarify, since Oto happened to post almost the same solution a bit faster:
DISTINCT is an SQL construct, a syntax element, not a function. Do not use parentheses like DISTINCT (customer_name). Would be short for DISTINCT ROW(customer_name) - a row constructor unrelated to DISTINCT - and just noise for the simple case with a single expression, because Postgres removes the pointless row wrapper for a single element automatically. But if you wrap more than one expression like that, you get an actual row type - an anonymous record actually, since no row type is given. Most certainly not what you want.
What is a row constructor used for?
Also, don't confuse DISTINCT with DISTINCT ON (expr, ...). See:
Select first row in each GROUP BY group?
Try something like...
SELECT `order_dtls`.*
FROM `order_dtls`
INNER JOIN `order_dtls` AS mirror
ON `order_dtls`.`Order_id` <> `mirror`.`Order_id`
AND `order_dtls`.`Customer_name` = `mirror`.`Customer_name`
AND DATEDIFF(`order_dtls`.`Order_date`, `mirror`.`Order_date`) = 1
The way I would think of it doing it would be to join the table the date part with itselft on the next date and joining it with the Customer_name too.
This way you can ensure that the same customer_name done an order on 2 consecutive days.
For MySQL:
SELECT distinct *
FROM order_dtls t1
INNER JOIN order_dtls t2 on
t1.Order_date = DATE_ADD(t2.Order_date, INTERVAL 1 DAY) and
t1.Customer_name = t2.Customer_name
The result you should also select it with the Distinct keyword to ensure the same customer is not displayed more than 1 time.
For postgresql:
select distinct(Customer_name) from your_table
where exists
(select 1 from your_table t1
where
Customer_name = your_table.Customer_name and Order_date = your_table.Order_date+1 )
Same for MySQL, just instead of your_table.Order_date+1 use: DATE_ADD(your_table.Order_date , INTERVAL 1 DAY)
This should work:
SELECT A.customer_name
FROM order_dtls A
INNER JOIN (SELECT customer_name, order_date FROM order_dtls) as B
ON(A.customer_name = B.customer_name and Datediff(B.Order_date, A.Order_date) =1)
group by A.customer_name

SQL Server select max date per ID

I am trying to select max date record for each service_user_id for each finance_charge_id and the amount that is linked the highest date
select distinct
s.Finance_Charge_ID, MAX(s.start_date), s.Amount
from
Service_User_Finance_Charges s
where
s.Service_User_ID = '156'
group by
s.Finance_Charge_ID, s.Amount
The issue is that I receive multiple entries where the amount is different. I only want to receive the amount on the latest date for each finance_charge_id
At the moment I receive the below which is incorrect (the third line should not appear as the 1st line has a higher date)
Finance_Charge_ID (No column name) Amount
2 2014-10-19 1.00
3 2014-10-16 500.00
2 2014-10-01 1000.00
Remove the Amount column from the group by to get the correct rows. You can then join that query onto the table again to get all the data you need. Here is an example using a CTE to get the max dates:
WITH MaxDates_CTE (Finance_Charge_ID, MaxDate) AS
(
select s.Finance_Charge_ID,
MAX(s.start_date) MaxDate
from Service_User_Finance_Charges s
where s.Service_User_ID = '156'
group by s.Finance_Charge_ID
)
SELECT *
FROM Service_User_Finance_Charges
JOIN MaxDates_CTE
ON MaxDates_CTE.Finance_Charge_ID = Service_User_Finance_Charges.Finance_Charge_ID
AND MaxDates_CTE.MaxDate = Service_User_Finance_Charges.start_date
This can be done using a window function which removes the need for a self join on the grouped data:
select Finance_Charge_ID,
start_date,
amount
from (
select s.Finance_Charge_ID,
s.start_date,
max(s.start_date) over (partition by s.Finance_Charge_ID) as max_date,
s.Amount
from Service_User_Finance_Charges s
where s.Service_User_ID = 156
) t
where start_date = max_date;
As the window function does not require you to use group by you can add any additional column you need in the output.

SQL query count divided by a distinct count of same query

Having some trouble with some SQL.
Take the following result for instance:
LOC_CODE CHANNEL
------------ --------------------
3ATEST-01 CHAN2
3ATEST-01 CHAN3
3ATEST-02 CHAN4
What I need to do is get a count of the above query, grouped by channel, but i want that count to be divided by the count that the "LOC_CODE" appears.
Example of the result I am after is:
CHANNEL COUNT
---------------- ----------
CHAN2 0.5
CHAN3 0.5
CHAN4 1
Above explaination is that the CHAN2 appears next to "3ATEST-01", but that LOC_CODE of "3ATEST-01" appears twice, so the count should be divided by 2.
I know I can do this by basically duplicating the query with a distinct count, but the underlying query is quite complex and don't really want to harm performance.
Please let me know if you would like more information!
Try:
select channel,
count(*) over (partition by channel, loc_code)
/ count(*) over (partition by loc_code) as count_ratio
from my_table
SELECT t.CHANNEL, COUNT(*) / gr.TotalCount
FROM my_table t JOIN (
SELECT LOC_CODE, COUNT(*) TotalCount
FROM my_table
GROUP BY LOC_CODE
) gr USING(LOC_CODE)
GROUP BY t.LOC_CODE, t.CHANNEL
Create a index on (LOC_CODE, CHANNEL)
If are no duplicate channels, replace COUNT(*) / gr.TotalCount with 1 / gr.TotalCount and remove the GROUP BY clause
First, find a query that gets you the correct results. Then, see if it can be optimised. My guess is that it's hard to optimise as you require two different groupings, one per Channel and one pre Loc_Code.
I'm not even sure that this fits your description:
SELECT t.CHANNEL
, COUNT(*) / SUM(grp.TotalCount)
FROM my_table t
JOIN
( SELECT LOC_CODE
, COUNT(*) TotalCount --- or is it perhaps?:
--- COUNT(DISTINCT CHANNEL)
FROM my_table
GROUP BY LOC_CODE
) grp
ON grp.LOC_CODE = t.LOC_CODE
GROUP BY t.CHANNEL
Your requirements are still a bit unclear to me when it comes to duplicate CHANNELs, but this should work if you want grouping on both CHANNEL and LOC_CODE to sum up later;
SELECT L1.CHANNEL, 1/COUNT(L2.LOC_CODE)
FROM Locations L1
LEFT JOIN Locations L2 ON L1.LOC_CODE = L2.LOC_CODE
GROUP BY L1.CHANNEL, L1.LOC_CODE
Demo here.