ORDER BY ON A FORMATTED DATE (TO_CHAR) - sql

I am using metabase and I need your help on a query please.
SELECT
CS.ID,
OG.NAME,
CS.REASON,
TO_CHAR(CS.ACR::DATE, 'DD/MM/YYYY') AS ACR,
TO_CHAR(OG.S_DATE::DATE, 'DD/MM/YYYY') AS S_DATE,
TO_CHAR(OG.D_DATE::DATE, 'DD/MM/YYYY') AS D_DATE
FROM
TABLE1 CS,
TABLE 2 OG
WHERE
CS.OGA_ID = OG.ID
ORDER BY
ACR ASC
The filtered result is :
01/01/2023
01/02/2022
01/04/2022
01/07/2022
01/09/2022
01/10/2021
01/12/2021
Q. What is not very clear, do you know why it sorts based on the month or day only, I do not understand and how to remedy this?
Thank you
I would like to sort the dates in ascending order.

Related

SQL Snowflake Min Date For Each Record & Group By Month

I am using Snowflake to get some information from the data. I have a table in which the same employee (PRS_ID) can be included in many rows and I want to take the EARLIEST date (BGN_DATE) for each employee (PRS_ID). Next, I want to see how many employees I have each month.
Ideally if I could have in YYYY-MM format (YYYYMM will work too). What I was working with is below:
SELECT PRS_ID, MIN(BGN_DATE), MONTH(BGN_DATE)
FROM myTable
WHERE EMP_STS = 'T'
GROUP BY PRS_ID, MONTH(BGN_DATE)
However, this will show me the data per employee (PRS_ID), which is too granular (as explained above). But when I remove "PRS_ID" from grouping I get below error:
SQL compilation error: error line 1 at position 7 'myTable.PRS_ID' in select clause is neither an aggregate nor in the group by clause.
Does anyone know how to fix it?
Thank you
Sample Data:
PRS_ID
EMP_STS
BGN_DATE
homsimps
T
2022-01-30
homsimps
T
2022-02-28
homsimps
T
2022-03-30
bartsimps
T
2022-01-30
bartsimps
T
2022-02-28
bartsimps
T
2022-03-31
lisasimps
T
2022-04-30
lisasimps
T
2022-05-31
lisasimps
T
2022-06-30
lisasimps
T
2022-07-30
margesimps
T
2022-02-28
margesimps
T
2022-03-30
Expected Outcome:
Period
Count
2022-01
2
2022-02
1
2022-03
0
2022-04
1
Using aggregation twice:
WITH cte AS (
SELECT PRR_ID, MIN(BGN_DATE) AS min_bgn_date
FROM my_table
WHERE EMP_STS = 'T'
GROUP BY PRS_ID
)
SELECT TO_VARCHAR(min_bgn_date, 'YYYYMM') AS month, COUNT(*) AS cnt
FROM cte
GROUP BY TO_VARCHAR(min_bgn_date, 'YYYYMM');
-- GROUP BY month
There is a simpler function here, DATE_TRUNC will allow you to convert dates to months. You can then convert to the format you'd like.
WITH MY_CTE AS (
SELECT
PRS_ID,
DATE_TRUNC(MONTH, MIN(BGN_DATE)) AS MONTH_START
FROM
myTable
WHERE
EMP_STS = 'T'
GROUP BY 1
)
SELECT
TO_CHAR(MONTH_START, 'yyyy-mm') AS PERIOD,
COUNT(PRS_ID)
GROUP BY 1

how to display the total sum of all individual dates in Oracle

I have a query that should display the total sum of sales of all individual dates,not the separate sales in each day. Below is the query I have tried and I am attaching a sample image of the output that I have gotten from this query. Your help would be appreciated.
SELECT sc_cd,Mon,sum(NET_SAL) SALE
FROM (SELECT TO_CHAR(to_date(deli_DT),'Mm') mm,
sc_cd,
TO_CHAR(to_date(deli_DT),'dd-Mon-yy') Mon,
sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in ('22')
AND deli_dt BETWEEN '01-aug-15' and '31-aug-15'
AND CANCL IS NULL
AND sc_cd='01'
GROUP BY TO_CHAR(to_date(deli_DT),'Mm'),
SC_cd,
TO_CHAR(to_date(deli_DT),'dd-Mon-yy'),
sale_net)
ORDER BY 3;
You have the sale_net column in the group_by clause, so you will still see one row for each value - you'll only see any actual aggregation if you have two source rows with the same value. Remove that from the group by. It's also not clear why you're using a subquery; if you don't want MM in the output, just don't select it in the first place:
SELECT sc_cd,TO_CHAR(deli_DT,'dd-Mon-yy') Mon,sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in('22')
and deli_dt BETWEEN '01-aug-15' and '31-aug-15'
and CANCL IS NULL
AND sc_cd='01'
GROUP BY SC_cd,TO_CHAR(deli_DT,'dd-Mon-yy')
order by 3
You should perhaps be selecting and grouping by trunc(deli_DT) rather than TO_CHAR(to_date(deli_DT),'dd-Mon-yy'), but if you need to format it anyway then it might not matter. But if deli_DT is a date field - as it seems to be, though it isn't entirely clear - then you should not be doing to_date() on it at all, as Boneist commented. You're really doing to_date(to_char(deli_dt)), with two implicit conversions using your NLS_DATE_FORMAT.
Using strings for your filter isn't a good idea though, and neither is using two-digit years; and you won't be seeing any rows which are from 2015-08-31 but after midnight; you should use explicit date conversions or literals, and use greater than/less than instead of between:
and deli_dt >= to_date('01-aug-2015', 'DD-mon-YYYY')
and deli_dt < to_date('01-sep-2015', 'DD-mon-YYYY'
Or:
and deli_dt >= date '2015-08-01'
and deli_dt < date '2015-09-01'
The issue is simple, you must eliminate sale_net column from group by clause.
Additionaly, if deli_DT is a Date datatype, you should write the query without to_date function. Also, you don't need two group by clauses:
SELECT
TO_CHAR(deli_DT,'Mm') mm,
sc_cd,
TO_CHAR(deli_DT,'dd-Mon-yy') Mon,
sum(sale_net) NET_SAL
from bill_mas
where sc_cd not in('22')
and deli_dt BETWEEN '01-aug-15' and '31-aug-15'
and CANCL IS NULL
AND sc_cd='01'
GROUP BY TO_CHAR(deli_DT,'Mm'), sc_cd, TO_CHAR(deli_DT,'dd-Mon-yy')
order by 3;
If you are using oracle and want sales by day you can use analytic function like-
sum(net_sale) over(partition by mon) from your_table;
it will give you sale of each day like I have mad e a temp1 table like this-
id mon net_sale
1 05-08-15 123
1 05-08-15 23
1 05-08-15 1
1 12-08-15 23
1 12-08-15 455
1 12-08-15 122
and the output is like-
Mon net_sale
05-08-15 147
05-08-15 147
05-08-15 147
12-08-15 600
12-08-15 600
12-08-15 600

how to split single row into multiple row in db2?

This is what i have in table xyz
NAME AMOUNT BEGIN_DATE END_DATE
ABC 5.0 2013-05-11 2014-06-20
following is what i want using IBM DB2 database
NAME AMOUNT BEGIN_DATE END_DATE
ABC 5.0 2013-05-11 2013-12-31
ABC 5.0 2014-01-01 2014-06-30
instead of just one row from xyz table, i need to fetch 2 rows as above output.
How do I split one row into two ?
The following will only list rows where the begin and end dates span exactly two years or within the same year.
SELECT
NAME,
AMOUNT,
BEGIN_DATE,
DATE(YEAR(BEGIN_DATE)||'-12-31') AS END_DATE
FROM xyz
WHERE YEAR(END_DATE)-YEAR(BEGIN_DATE)=1
UNION
SELECT
NAME,
AMOUNT,
DATE(YEAR(END_DATE)||'-01-01') AS BEGIN_DATE,
END_DATE
FROM xyz
WHERE YEAR(END_DATE)-YEAR(BEGIN_DATE)=1
UNION
SELECT
NAME,
AMOUNT,
BEGIN_DATE,
END_DATE
FROM xyz
WHERE YEAR(END_DATE)-YEAR(BEGIN_DATE)=0
ORDER BY BEGIN_DATE
You can make two SQL statements, to select the first, using '2013-12-31' as a constant for the end-date, then to select a second time, using '2014-01-01' as a constant start date. Then use UNION ALL to put them together.
If you also have some records that start and end within 2013, and therefore do not need to be split, you can get those separately, and exclude them from the other two queries. Other variations in your data might require some extra conditions, but this example should get you going:
select NAME, AMOUNT, BEGIN_DATE, END_DATE
from xyz
where END_DATE <= '2013-12-31'
UNION ALL
select NAME, AMOUNT, BEGIN_DATE, '2013-12-31'
from xyz
where END_DATE >= '2014-01-01'
UNION ALL
select NAME, AMOUNT, '2014-01-01', END_DATE
from xyz
where END_DATE >= '2014-01-01'

PL/SQL Finding Difference Between Start and End Dates in Different Rows

I am trying to find the difference between start and end dates in different rows of a result set, using PL/SQL. Here is an example:
ID TERM START_DATE END_DATE
423 201420 26-AUG-13 13-DEC-13
423 201430 21-JAN-14 09-MAY-14
423 201440 16-JUN-14 07-AUG-14
For any specific ID, I need to get the difference between the end date in the first record and the start date of the second record. Similarly, I need to get the difference between the end date in the second record and the start date of the third record, and so forth.
Eventually I will need to perform the same operation on a variety of IDs. I am assuming I have to use a cursor and loop.
I would appreciate any help or suggestions on accomplishing this. Thanks in advance.
The "lead" analytic function in Oracle can grab a value from the succeeding row as a value in the current row.
Given a series of rows returned from a query and a position of the cursor, LEAD provides access to a row at a given physical offset beyond that position.
Here, this SQL grabs start_date from the next row and subtracts end_date from the current row.
select id, term, start_date, end_date,
lead(start_date) over (partition by id order by term) - end_date diff_in_days
from your_table;
Sample output:
ID TERM START_DATE END_DATE DIFF_IN_DAYS
---------- ---------- -------------------- -------------------- ------------
423 201420 26-AUG-2013 00:00:00 13-DEC-2013 00:00:00 39
423 201430 21-JAN-2014 00:00:00 09-MAY-2014 00:00:00 36
423 201440 14-JUN-2014 00:00:00 07-AUG-2014 00:00:00
I would suggest looking at using the LEAD and LAG analytic functions from Oracle. By the sounds of it they should suit your needs.
See the docs here: http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions074.htm
Code:
SELECT [ID], [TERM], [START_DATE], [END_DATE],
CASE WHEN MIN([END_DATE]) OVER(PARTITION BY [ID] ORDER BY [TERM] ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)=[END_DATE] THEN NULL ELSE
MIN([END_DATE]) OVER(PARTITION BY [ID] ORDER BY [TERM] ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)-[START_DATE] END AS [DAYS_BETWEEN]
FROM [TABLE]
This seemed to work:
SELECT DISTINCT
ID,
TERM_CODE,
TERM_START_DATE,
TERM_END_DATE,
( ( LEAD ( TERM_START_DATE, 1 ) OVER ( PARTITION BY ID ORDER BY TERM_CODE ) ) -TERM_END_DATE AS DIFF DAYS
FROM TABLE

How to Group SQL Results by Date in the Actual Order that they Appear in the Year in Oracle?

I'm trying to figure out how I can group the SQL results from a table in Oracle in the actual order in which the month occurred instead of the alphabet order of their names.
Can anyone help me with this? Thanks in advanced.
============ UPDATE ============
Here's an example query:
SELECT exec_month FROM table_or_view WHERE condition=1 GROUP BY exec_month;
You see, the problem is that I can't group by month like I want to. Just as Vincent Malgrat said, "Grouping has no implied order".
Grouping has no implied order. Add an ORDER BY clause if you want to display the rows in a certain order:
SQL> with data as (
2 SELECT add_months(trunc(sysdate, 'year'), rownum-1) dt
3 FROM dual CONNECT BY LEVEL <= 4
4 )
5 select to_char(dt, 'month')
6 from data
7 group by to_char(dt, 'month'), trunc(dt, 'month')
8 order by trunc(dt, 'month');
TO_CHAR(D
---------
january
february
march
april