getting duplicate rows - sql

If I run the below query I am getting duplicate rows. Could anyone please help .
using distinct making query running long. never ending
adate -> 5-may-2022 rdate -> 28th -may-2022
query output getting as
7-may-2022
14-may-2022
21-may-2022
21-may-2022
21-may-2022
code is as below
select * from (
with qry as (
SELECT /* + parallel(12) */ adate AS DATE1,
MIN(rdate) AS DATE2,
a.id
FROM
t1 a,
t2 b,
t3 c
WHERE
a.id = b.id
GROUP BY
a.id,adate
)select * from (
select id,
date1 + level - 1 as CurDate,date1,date2
from qry
connect by level <= (date2 - date1) + 1)
--group by id,date1 + level - 1,date1,date2 having count(date1 + level - 1)>1)
where to_char(curdate,'DY','NLS_DATE_LANGUAGE=ENGLISH') IN('SAT')
and (CurDate) NOT IN(date1,date2)) dual;

Wrong use of generators... Read more:
https://www.orafaq.com/wiki/Oracle_Row_Generator_Techniques
https://blogs.oracle.com/sql/post/row-generators-part-2
https://blogs.oracle.com/sql/post/follow-up-to-row-generators-part-2
Fix with minimal changes:
select * from (
with qry as (
SELECT /* + parallel(12) */ adate AS DATE1,
MIN(rdate) AS DATE2,
a.id
FROM
t1 a,
t2 b,
t3 c
WHERE
a.id = b.id
GROUP BY
a.id,adate
)select * from (
select id,
date1 + lvl - 1 as CurDate,date1,date2
from qry
,lateral(select level lvl from dual connect by level <= (date2 - date1) + 1)
--group by id,date1 + level - 1,date1,date2 having count(date1 + level - 1)>1)
where to_char(curdate,'DY','NLS_DATE_LANGUAGE=ENGLISH') IN('SAT')
and (CurDate) NOT IN(date1,date2)) dual;

Related

Avoid division by zero: 1 / 0 error in WITH clause

I am using the following in a WITH clause to create a FULL JOIN in Big Query:
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / (1 - (Val2 + Val3))) AS Calc1,
FROM `project.dataset.table1`
GROUP BY Date
),
b as (SELECT
date AS Date,
FROM `project.dataset.table2`
GROUP BY Date
)
SELECT a.Date, SUM(Calc1)
FULL JOIN a on b.Date = a.Date
GROUP BY b.Date
Calc1 is creating a 'division by zero: 1 / 0' error, and I can't seem to work out how to restructure this so it doesn't occur. The query works fine outside of the WITH clause, as I can simply not include the GROUP BY so have no need to SUM Calc1?
Below is for BigQuery Standard SQL
Use
SUM(SAFE_DIVIDE(Val1, 1 - (Val2 + Val3))) AS Calc1
instead of
SUM(Val1 / (1 - (Val2 + Val3))) AS Calc1
Use NULLIF :
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / NULLIF((1 - (Val2 + Val3)),0)) AS Calc1,
FROM `project.dataset.table1`
GROUP BY Date, SUM(Calc1)
)
Have you analysed your data within "table1" to ensure that Val1, Val2 & Val3 are consistently populated, or do you have NULL values?
This could be the issue with your subtraction from 1.
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / (1 - (isnull(Val2,0.00) + isnull(Val3,0.00)))) AS Calc1
FROM `project.dataset.table1`
GROUP BY Date
),
b as (
SELECT
date AS Date,
FROM `project.dataset.table2`
GROUP BY Date
)
SELECT a.Date, SUM(a.Calc1)
FULL JOIN a on b.Date = a.Date`enter code here`
GROUP BY b.Date

SQL query from Oracle SQL to T-SQL

I have a subquery which is used for an Oracle database, but I want to use an equivalent query for a SQL Server database.
I didn't figure out how to migrate the TO_TIMESTAMP(TO_CHAR(TO_DATE part and also didn't know how to handle the thing with rownums in T-SQL.
Is it even possible to migrate this query?
SELECT 0 run_id,
0 tran_id,
0 sort_id,
' ' tran_type,
10 prod_id,
72 type_id,
1 value,
TO_TIMESTAMP(TO_CHAR(TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1, 'YYYY.MM.DD') || to_char(sw.end_time, 'HH24:MI:SS'), 'YYYY.MM.DD HH24:MI:SS') event_publication,
EXTRACT (YEAR
FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) y,
EXTRACT (MONTH
FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) mo,
EXTRACT (DAY
FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) d,
to_number(to_char (sw.end_time, 'HH24')) h,
to_number(to_char (sw.end_time, 'MI')) mi,
to_number(to_char (sw.end_time, 'SS')) s,
0 ms
FROM all_objects ao,
settlement_win sw,
prod_def pd
WHERE pd.prod_id = 10
AND sw.country = pd.country
AND sw.commodity = pd.commodity
AND rownum <= TO_DATE('2016-03-18 23:59:00', 'YYYY.MM.DD HH24:MI:SS') -TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS')+1
The first thing to address is the use of rownum which has no direct equivalent in TSQL but we can mimic it, and for this particular query you need to recognize that the table ALL_OBJECTS is only being used to produce a number of rows. It has no other purpose to the query.
In TSQL we can generate rows using a CTE and there are many many variants of this, but for here I suggest:
;WITH
cteDigits AS (
SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
)
, cteTally AS (
SELECT
d1s.digit
+ d10s.digit * 10
+ d100s.digit * 100 /* add more like this as needed */
-- + d1000s.digit * 1000 /* add more like this as needed */
+ 1 AS rownum
FROM cteDigits d1s
CROSS JOIN cteDigits d10s
CROSS JOIN cteDigits d100s /* add more like this as needed */
--CROSS JOIN cteDigits d1000s /* add more like this as needed */
)
This will quickly spin-up 1000 rows as is and can be extended to produce many more rows by adding more cross joins. Note this returns a column called rownum which starts at 1 thus mimicking the Oracle rownum.
So next you can just add some of the remaining query, like this:
SELECT
0 run_id
, 0 tran_id
, 0 sort_id
, ' ' tran_type
, 10 prod_id
, 72 type_id
, 1 value
, convert(varchar, dateadd(day, rownum - 1,'20160318'),121) event_publication
-- several missing rows here
, 0 ms
FOM cteTally
INNER JOIN settlement_win sw
INNER JOIN prod_def pd ON sw.country = pd.country AND sw.commodity = pd.commodity
WHERE pd.prod_id = 10
AND rownum <= datediff(day,'20160318','20160318') + 1
Note that you really do not need a to_timestamp() equivalent you just need the ability to output date and time to the maximum precision of your data which appears to be to the level of seconds.
To progress further (I think) requires an understanding of the data held in the column sw.end_time. If this can be converted to the mssql datetime data type then it is just a matter of adding a number of days to that value to arrive at the event_publication and similarly if sw.end_time is converted to a datetime data type then use date_part() to get the hours, minutes and seconds from that column. e.g.
, DATEADD(day,rownum-1,CONVERT(datetime, sw.end_time)) AS event_publication
also, if such a calculation works then it would be possible to use an apply operator to simplify the overall query, something like this
;WITH
cteDigits AS (
SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
)
, cteTally AS (
SELECT
d1s.digit
+ d10s.digit * 10
+ d100s.digit * 100 /* add more like this as needed */
-- + d1000s.digit * 1000 /* add more like this as needed */
+ 1 AS rownum
FROM cteDigits d1s
CROSS JOIN cteDigits d10s
CROSS JOIN cteDigits d100s /* add more like this as needed */
--CROSS JOIN cteDigits d1000s /* add more like this as needed */
)
SELECT
0 run_id
, 0 tran_id
, 0 sort_id
, ' ' tran_type
, 10 prod_id
, 72 type_id
, 1 value
, convert(varchar(23), CA.Event_publication, 121) Event_publication
, datepart(day,CA.Event_publication) dd
, datepart(month,CA.Event_publication) mm
, datepart(year,CA.Event_publication) yyyy
, datepart(hour,CA.Event_publication) hh24
, datepart(minute,CA.Event_publication) mi
, datepart(second,CA.Event_publication) ss
, 0 ms
FOM cteTally
INNER JOIN settlement_win sw
INNER JOIN prod_def pd ON sw.country = pd.country AND sw.commodity = pd.commodity
CROSS APPLY (
SELECT DATEADD(day,rownum-1,CONVERT(datetime, sw.end_time)) AS event_publication ) CA
WHERE pd.prod_id = 10
AND rownum <= datediff(day,'20160318','20160318') + 1
NB: IT may be necessary to include this datediff(day,'19000101,'20160318') (which equals 42445) into the calculation of the event_date e.g.
SELECT DATEADD(day,42445 + (rownum-1),CONVERT(datetime, sw.end_time)) AS event_publication
One last point is that you could use datetime2 instead of datetime if you really do need a greater degree of time precision but there is no easily apparent requirement for that.

what is the divisor is equal to zero in plsql?

SELECT pstartdate,
opbal,
joined,
resign,
( opbal + joined - resign ) clbal
,
( Round(( ( resign * 100 ) / ( opbal + joined ) ) / 100, 2) * 100
) attriation
FROM (SELECT pstartdate,
penddate,
Getopempbal(pstartdate) OpBal,
Getempjn(pstartdate, penddate) Joined,
Getempres(pstartdate, penddate) Resign
FROM (SELECT Add_months(:startdate, LEVEL - 1) pstartdate,
Add_months(:startdate, LEVEL) - 1 penddate
FROM dual
CONNECT BY LEVEL <= Months_between( :enddate, :startdate ) + 1))
ORDER BY To_number(1)
When i executed this query Error came
divisor is equal to zero.
i think error this place
(round(((resign*100)/(opbal+joined))/100,2)*100) attriation
I recommend that you use the nullif() function when doing division:
select . . .
( Round(( ( resign * 100 ) / nullif( opbal + joined, 0 ) ) / 100, 2) * 100
) attriation
This returns NULL if the denominator is zero.
SELECT pstartdate,
opbal,
joined,
resign,
(opbal+joined-resign) clbal,
round((**NULLIF**(resign,0) *100)/(opbal + joined)) ab
FROM
(SELECT pstartdate,
penddate,
getopempbal(pstartdate) opbal,
getempjn(pstartdate,penddate) joined,
getempres(pstartdate,penddate) resign
FROM
(SELECT add_months(:startdate, LEVEL-1) pstartdate,
add_months(:startdate, LEVEL)-1 penddate
FROM dual CONNECT BY LEVEL <= months_between(:enddate, :startdate) + 1))
ORDER BY to_number(1)

To ensure uniqueness of parent record(s)

How can I ensure that I am getting uniqueness on the parent record? I am wanting to get a count of mopid + user on a day by day count. How can I do this? Here is my code so far, I just have low confidence that it isn't giving me uniqueness on a mopid + user + day.
SELECT TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD') DayWorked, MOPNOTES.MOPNOTEUSER, MOPNOTES.mopid, COUNT(*) AS DAILY
FROM MOPUSER.MOPACTIVITY
INNER JOIN MOPUSER.MOPNOTES
ON MOPACTIVITY.MOPID=MOPNOTES.MOPID
WHERE MOPNOTES.MOPNOTEDATE > TO_DATE('01-JUL-13', 'DD-MON-YY') AND MOPNOTES.MOPNOTEDATE< TO_DATE('01-AUG-13', 'DD-MON-YY')
AND MOPACTIVITY.MOPSERVICEIMPACTED <> 'VOICE'
AND MOPACTIVITY.MOPSERVICEIMPACTED <> 'PWR/ENV'
AND (MOPNOTES.MOPNOTEUSER LIKE '%Ramesh%'
OR MOPNOTES.MOPNOTEUSER LIKE '%Saravanan%'
OR MOPNOTES.MOPNOTEUSER LIKE '%Boominathan%'
OR MOPNOTES.MOPNOTEUSER LIKE '%Srinivasan%'
OR MOPNOTES.MOPNOTEUSER LIKE '%Sathya%')
GROUP BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
ORDER BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
By definition, GROUP BY will give you unique rows per combination of columns grouped by. Since you are grouping by mopid + user + day, you will have uniqueness on mopid + user + day.
To illustrate, this query:
SELECT a, b
FROM ( SELECT MOD(LEVEL, 2) a, MOD(LEVEL, 4) b FROM DUAL CONNECT BY LEVEL < 11 )
GROUP BY a, b
... will give you the same results as this query:
SELECT DISTINCT a, b
FROM ( SELECT MOD(LEVEL, 2) a, MOD(LEVEL, 4) b FROM DUAL CONNECT BY LEVEL < 11 )
If you really want to be sure, you could verify that the counts match between the following two queries:
SELECT COUNT(1) FROM (
SELECT TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD') DayWorked, MOPNOTES.MOPNOTEUSER, MOPNOTES.mopid, COUNT(*) AS DAILY
FROM ...[etc]...
GROUP BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
--ORDER BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
)
SELECT COUNT(1) FROM (
SELECT DISTINCT ROWNUM, TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD') DayWorked, MOPNOTES.MOPNOTEUSER, MOPNOTES.mopid --, COUNT(*) AS DAILY
FROM ...[etc]...
--GROUP BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
--ORDER BY TO_CHAR(MOPNOTES.MOPNOTEDATE, 'YYYY-MM-DD'),MOPNOTES.MOPNOTEUSER,MOPNOTES.MOPID
)

Connect by level for SQL Server 2008 like Oracle

nDays := Round( dEndTime - dStartTime ) + 1;
For i in 1..7 Loop
nDay := i + 1;
if i = 7 Then
nDay := 1;
End If;
SELECT To_Date(To_Char((dStartTime+Level-1),'DD.MM.YYYY')||' 00:00','DD.MM.YYYY HH24:MI'),
To_Date(To_Char((dStartTime+Level-1),'DD.MM.YYYY')||' 23:59','DD.MM.YYYY HH24:MI')
FROM DUAL
WHERE To_Char( dStartTime + Level -1 , 'd' ) = To_Char(nDay)
CONNECT BY Level <= nDays;
End Loop;
output:
22-JUL-12
23-JUL-12
18-JUL-12
19-JUL-12
20-JUL-12
21-JUL-12
I need to convert this query to SQL Server 2008, please help to find workaround with the same......
I have tried above output with single query with nDay from 1 to 7.....
You can do this with a recursive CTE. However, that syntax can be hard to remember, so when I need a handful of items, I do something like:
select *
from (select row_number() over (order by (select NULL)) as seqnum
from information_schema.columns
) t
where seqnum < <value>
Any table can be used. I just put in the INFORMATION_SCHEMA.columns table, because it is easy and generally has dozens or hundreds of rows.
Your output doesn't match the query. The following generates all dates between two values that are reasonably close together, Here is an example:
declare #dstarttime date = '2012-07-11', #dendtime date= '2012-07-13';
with const as (select #dstarttime as dStartTime, #dendtime as dendTime)
SELECT DATEADD(d, seqnum - 1, dStartTime)
FROM (select *
from (select row_number() over (order by (select NULL)) as seqnum, const.*
from information_schema.columns cross join const
) t
where seqnum <= DATEDIFF(d, dStartTime, dendTime) + 1
) t
As I said, you can also do this with recursive CTEs or, if you have one, with a calendar table.