I am a beginner in SQL and having a problem to get my query to work. All I need to do is to add a date range filter to the query below. I am filtering by the field f.date_value. Parm 2 and Parm 3 will be the date that staff will choose when they run the report. This SQL report run into a built-in SQL tool inside one of our softwares so it is a little different than SQL Developer that we use to access the database. Anyone has any idea on what I need to change on the query below? Thank you very much!
SELECT st.dcid,
st.student_number,
st.lastfirst,
st.grade_level,
to_char(f.date_value,'MM/DD/YYYY'),
f.fee_type_name,
f.description,
f.fee_amount,
f.fee_paid,
f.fee_balance
FROM PS.FEE f
LEFT OUTER JOIN STUDENTS ST
ON f.StudentID = st.ID
WHERE (f.SCHOOLID=%param1%) AND (st.ENROLL_STATUS=0) AND (f.date_value BETWEEN %parm2% AND %parm3%)
ORDER BY st.LASTFIRST
There isn't anything obvious to offer, except perhaps the use of to_date and exactly how you use that depends on the data type and format of the parameters. I assume they are strings, but don't know what format they are supplied (I have used yyyy-mm-dd below).
SELECT
st.dcid
, st.student_number
, st.lastfirst
, st.grade_level
, to_char(f.date_value, 'MM/DD/YYYY')
, f.fee_type_name
, f.description
, f.fee_amount
, f.fee_paid
, f.fee_balance
FROM PS.FEE f
LEFT OUTER JOIN STUDENTS st ON f.StudentID = st.ID
WHERE f.SCHOOLID = %param1%
AND st.ENROLL_STATUS = 0
AND f.date_value BETWEEN to_date(%parm2%,'yyyy-mm-dd') AND to_date(%parm3%,'yyyy-mm-dd')
ORDER BY
st.LASTFIRST
The only other thing to note is that between is often a poor way to define a date range - but this in part depends on the time precision of your data. IF your data is accurate to the day only, then between as you see above is ok. If there is lower level of precision (e.g. second or lower) then don't use between, instead:
AND f.date_value >= to_date(%parm2%,'yyyy-mm-dd')
AND f.date_value < to_date(%parm3%,'yyyy-mm-dd') + 1
Related
So I have a "Sample", "Test" and "Result" table linked to each other from a database and I am trying to pull information using MS Query. Each sample has one test and each test could have roughly 20 results entered by different people attached to it.
What I want is for the sample to only display if the person's name I enter is NOT involved with entering ANY of the results.
SELECT SAMPLE.SAMPLE_NUMBER, SAMPLE.TEXT_ID, SAMPLE.STATUS, SAMPLE.DATE_COMPLETED, SAMPLE.LOCATION, TEST.ANALYSIS, RESULT.ENTERED_BY
FROM DATABASE.RESULT RESULT, DATABASE.SAMPLE SAMPLE, DATABASE.TEST TEST
WHERE TEST.SAMPLE_NUMBER = SAMPLE.SAMPLE_NUMBER AND RESULT.TEST_NUMBER = TEST.TEST_NUMBER
AND ((TEST.ANALYSIS='ID_META' Or TEST.ANALYSIS='ID_RIBO' Or TEST.ANALYSIS='ID_BACTERIA' Or TEST.ANALYSIS='ID_MOULD')
AND (SAMPLE.STATUS='C') AND (SAMPLE.DATE_COMPLETED Is Not Null)
AND (RESULT.ENTERED_ON Between [Start Date] And [End Date])
AND (RESULT.ENTERED_BY<>[Enter Name]))
ORDER BY SAMPLE.DATE_COMPLETED
This is the code that I have so far but the problem is if Alan has entered one of 10 results then that same sample will display 9 times and just not display for the one time he didn't enter a result. Is there a way that I can say if he entered ANY result at all then the sample won't appear at all.
Edit - To include additional clauses incorporated into the query. Query pulled directly from Excel connection window (from MS Query).
This answers the original version of the question.
You seem to be describing NOT EXISTS:
SELECT s.SAMPLE_NUMBER
FROM DATABASE.SAMPLE s
WHERE NOT EXISTS (SELECT 1
FROM DATABASE.RESULT r JOIN
DATABASE.TEST t
ON r.TEST_NUMBER = t.TEST_NUMBER
WHERE t.SAMPLE_NUMBER = s.SAMPLE_NUMBER AND
R.ENTERED_ON >= DATE '2020-02-01' AND
R.ENTERED_ON >= DATE '2020-02-03' AND
R.ENTERED_BY = 'ALAN'
) AND
S..DATE_COMPLETED Is Not Null ;
I have left in your additional conditions, even though they are not mentioned in the question.
Notes:
NEVER use commas in the FROM clause.
Always use proper, explicit, standard, readable JOIN syntax.
Use proper DATE constants in Oracle.
Don't use BETWEEN with DATE particularly in Oracle. The DATE datatype has a time component, which might not be visible when you look at the data.
Please, try with below query:
SELECT DISTINCT(SAMPLE.SAMPLE_NUMBER) as SAMPLE_NUMBER
FROM DATABASE.SAMPLE SAMPLE
LEFT OUTER JOIN DATABASE.TEST TEST ON TEST.SAMPLE_NUMBER = SAMPLE.SAMPLE_NUMBER
LEFT OUTER JOIN DATABASE.RESULT RESULT ON RESULT.TEST_NUMBER = TEST.TEST_NUMBER
WHERE ((SAMPLE.DATE_COMPLETED Is Not Null)
AND (RESULT.ENTERED_ON Between CAST('01-FEB-2020' as DATE) And CAST('02-FEB-2020' as DATE))
AND (RESULT.ENTERED_BY <> 'ALAN'))
I'm trying to create a report for a customer that is specific to whatever date range they're needing at the current time, but I'm having a couple issues.
The biggest one is, how do I get my user input prompts for Enter_Start_Date and Enter_End_Date to only appear once each? (ie Start: 01-JAN-18 and End: 28-FEB-18). I tried to research other questions on here, and there was a suggestion (that worked for that user) of doing a && with same variable name before the additional occurrences, but when I run the query, I still get prompted 4 times.
The other "problem" I'm having is the below query isn't returning the results for parts_used and h_repairs like it should.
SELECT
a.h_desc,
a.fic,
a.p_part,
a.d_part,
a.nomenclature,
a.qpe,
SUM(b.qty) AS parts_used,
COUNT(c.fic) AS h_repairs,
a.cdf_ohb,
a.bmc
FROM
t_table_a a
LEFT JOIN t_table_b b ON b.fic = a.fic
AND b.part_no = a.d_part
AND b.real_f_date BETWEEN TO_DATE('&Enter_Start_Date', 'DD-MON-YY') AND TO_DATE('&Enter_End_Date'
, 'DD-MON-YY')
LEFT JOIN t_table_c c ON c.fic = a.fic
AND c.real_f_date BETWEEN TO_DATE('&&Enter_Start_Date', 'DD-MON-YY') AND TO_DATE('&&Enter_End_Date',
'DD-MON-YY')
GROUP BY
a.bmc,
a.cdf_ohb,
a.d_part,
a.fic,
a.h_desc,
a.nomenclature,
a.p_part,
a.qpe
ORDER BY
a.fic,
a.p_part
When I do the above query, the parts_used and h_repairs columns are completely out of whack, but when I nest the joins like in the below query, the results populate as expected.
SELECT
t.h_desc,
t.fic,
t.p_part,
t.d_part,
t.nomenclature,
t.qpe,
COUNT(c.fic) AS h_repairs,
t.parts_used,
t.cdf_ohb,
t.bmc
FROM
(
SELECT
a.h_desc,
a.fic,
a.p_part,
a.d_part,
a.nomenclature,
a.qpe,
SUM(b.qty) AS parts_used,
a.cdf_ohb,
a.bmc
FROM
t_table_a a
LEFT JOIN t_table_b b ON b.fic = a.fic
AND b.part_no = a.d_part
AND b.real_f_date BETWEEN TO_DATE('&Enter_Start_Date', 'DD-MON-YY') AND TO_DATE('&Enter_End_Date'
, 'DD-MON-YY')
GROUP BY
a.bmc,
a.cdf_ohb,
a.d_part,
a.fic,
a.h_desc,
a.nomenclature,
a.p_part,
a.qpe
) t
LEFT JOIN t_table_c c ON c.fic = t.fic
AND c.real_f_date BETWEEN TO_DATE('&&Enter_Start_Date', 'DD-MON-YY') AND TO_DATE('&&Enter_End_Date',
'DD-MON-YY')
GROUP BY
t.h_desc,
t.fic,
t.p_part,
t.d_part,
t.nomenclature,
t.qpe,
t.parts_used,
t.cdf_ohb,
t.bmc
ORDER BY
t.fic,
t.p_part
Is nesting the joins what needs to happen, or am I doing something a little wrong?
You've only changed the second reference to each substitution variable from & to &&. If you change all of them then you'll only be prompted once for each. (Not sure why it does that, but SQL*Plus does the same).
You can also prompt with a nicer message by adding these statements to you script before your query:
accept Enter_Start_Date date format "DD-MON-YY" prompt "Enter the start date in the format 'DD-MON-YY'"
accept Enter_End_Date date format "DD-MON-YY" prompt "Enter the end date in the format 'DD-MON-YY'"
As that defines the variables too, you can then use either a sing & or a double && when you refer to them (and you can use shorter names; and your own prompt text of course).
If you can I'd also suggest you choose a different date format, something which doesn't rely on the user's session date language (as the month abbreviations are NLS-dependent), preferably using 4-digit years, and which is unambiguous - so maybe YYYY-MM-DD instead of DD/MM/YYYY, though users could find that awkward I suppose. (That would also let you use date literals as bonus if you wanted to). As the prompt can contain the format you expect they at least know what they should enter, but they still might not like it.
I have two queries, the first is this:
SELECT * FROM "HOTELS"
LEFT JOIN "ROOM" ON "HOTELS"."HOTEL_ID"="ROOM"."HOTEL_ID"
LEFT JOIN "ROOM_TYPE" ON "ROOM"."ROOM_T"="ROOM_TYPE"."ROOM_T"
WHERE ("ROOM_TYPE"."ROOM_T"='Two beds' AND "HOTELS"."NAME"='Sunset Hotel');
The second is this:
SELECT * FROM "HOTELS"
LEFT JOIN "RESERVATIONS" ON "HOTELS"."HOTEL_ID"="RESERVATIONS"."HOTEL_ID"
WHERE ("HOTELS"."NAME"='Sunset Hotel' AND "RESERVATIONS"."DATE"='7/20/2016');
(I translated the strings from my native language)
OK, I am running the first one in Oracle's APEX SQL Commands page and it returns 4 entries, which are the rooms I am interested in.
I am running the second and it returns the reserved rooms for the date I am interested in, 1 row result.
Now I want to do the subtraction so that I get left with only the 3 rooms that are not reserved at that date.
But I try different formats that I found after googling and I always get some useless error messages.
You can use a left join and check for o matches:
SELECT *
FROM "HOTELS" h JOIN
"ROOM" r
ON h."HOTEL_ID" = r."HOTEL_ID" JOIN
"ROOM_TYPE" rt
ON r."ROOM_T" = rt."ROOM_T" LEFT JOIN
"RESERVATIONS" r
ON h."HOTEL_ID" r."HOTEL_ID" AND r."DATE" = '7/20/2016'
WHERE h."NAME= 'Sunset Hotel' AND
rt."ROOM_T"='Two beds' AND
r."HOTEL_ID" IS NULL;
You should not need LEFT JOIN for the hotels/rooms/room type connections. The ids should be valid -- and even if they are not, the check on room type requires matches anyway. The LEFT JOIN is needed for RESERVATIONS.
I always get some useless error messages
Error messages are never useless, in fact they are very useful to understand what's wrong your are doing or asking your RDBMS to do.
"RESERVATIONS"."DATE"='7/20/2016'
This is wrong since you are comparing a DATE with a string literal. Always, explicitly convert the string into date using TO_DATE and proper format mask. Or, if you dealing only with the date part and not concerned with the time portion, then use the ANSI date literal which uses a fixed format DATE 'YYYY-MM-DD'
Coming back to your original requirement about the query:
SELECT *
FROM "HOTELS" h
LEFT JOIN "ROOM" r
ON h."HOTEL_ID" = r."HOTEL_ID"
LEFT JOIN "ROOM_TYPE" t
ON r."ROOM_T" = t."ROOM_T"
LEFT JOIN "RESERVATIONS" v
ON ( h."HOTEL_ID" = v."HOTEL_ID"
AND v."DATE" = TO_DATE('2016-07-20', 'YYYY-MM-DD' )
WHERE t."ROOM_T" = 'Two beds'
AND h."NAME" = 'Sunset Hotel'
AND v."HOTEL_ID" IS NULL;
You can use a LEFT JOIN to include the RESERVATIONS table and put the date filter as part of the join condition (the other tables can use INNER JOINs):
SELECT *
FROM "HOTELS" h
INNER JOIN "ROOM" r
ON h."HOTEL_ID" = r."HOTEL_ID"
INNER JOIN "ROOM_TYPE" t
ON r."ROOM_T" = t."ROOM_T"
LEFT JOIN "RESERVATIONS" v
ON ( h."HOTEL_ID" = v."HOTEL_ID" AND v."DATE"= DATE '2016-07-20' )
WHERE t."ROOM_T" = 'Two beds'
AND h."NAME" = 'Sunset Hotel'
AND v."HOTEL_ID" IS NULL;
Then to test for a non-reservation you can check that the RESERVATIONS primary key is NULL (as per the last line).
Also note, that if you are comparing to a DATE data type then if you are using a string value Oracle will do an implicit TO_DATE() using the NLS_DATE_FORMAT parameter as the format mask - if this does not match then it will throw exceptions and if it does work and then the parameter gets changed it will break the query. It would be better to use a date literal value instead (e.g. DATE '2016-07-20').
Finally, you probably don't need to use double quotes around all the table and column names. By default, Oracle will convert column and table names to upper-case (which is what you are using so this isn't an issue) and the only reason to double-quote identifiers is if you want to specify a case-sensitive format (which you should try to avoid) or if you want to use keywords as names (which you also ought to avoid). So, if possible change the RESERVATIONS.DATE column to another name as DATE is a keyword in oracle (but the rest of the column names should not need quotes).
Another option is to use MINUS:
Subquery1
MINUS
Subquery2
https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries004.htm
I am trying to build some parametrized data source (sql query over jndi).
query of my data source is:
SELECT ${param_interval}(dim_date.date), count(docs_fact.id) as docs_count
FROM rel_docs_dates
left join docs_fact on rel_docs_dates.doc_id = docs_fact.id
left join dim_date on rel_docs_dates.date_id = dim_date.id
Parametr ${param_interval} can get two values: MONTH and DAY, and as i checked it got the correct values.
But when i am trying to make preview of my dashboard i get warning "error processing component".
Notice that this query (see bellow) works ok.
SELECT MONTH(dim_date.date), count(docs_fact.id) as docs_count, ${param_interval} as tmp_fiel
FROM rel_docs_dates
left join docs_fact on rel_docs_dates.doc_id = docs_fact.id
left join dim_date on rel_docs_dates.date_id = dim_date.id
Can somebody tell me where is mistake? Or (may be) this way to use parameters in data source is not supported?
finaly i found decision. it isnt what i would like to have but it works and it the most important thing.
i rewrite my query with 'case' constraction and, it is important, i changed type of my parametr from string to numeric (string doesnt work :( ). now my query looks like this:
SELECT
case ${param_interval}
when 1 then MONTH(dim_date.date)
when 2 then DAY(dim_date.date)
end
,count(docs_fact.id) as fact_count
FROM rel_docs_dates
left join docs_fact on rel_docs_dates.doc_id = docs_fact.id
left join dim_date on rel_docs_dates.date_id = dim_date.id
where dim_date.date > LAST_DAY(DATE_SUB(CURDATE(), INTERVAL ${param_period} MONTH))
AND dim_date.date < LAST_DAY(DATE_SUB(CURDATE(), INTERVAL 0 MONTH))
group by
case ${param_interval}
when 1 then MONTH(dim_date.date)
when 2 then DAY(dim_date.date)
end
order by YEAR(dim_date.date), MONTH(dim_date.date)
may be it will help somebody else.
I have created a query in MS Access to simulate a FULL OUTER JOIN and combine the results that looks something like the following:
SELECT NZ(estimates.employee_id, actuals.employee_id) AS employee_id
, NZ(estimates.a_date, actuals.a_date) AS a_date
, estimates.estimated_hours
, actuals.actual_hours
FROM (SELECT *
FROM estimates
LEFT JOIN actuals ON estimates.employee_id = actuals.employee_id
AND estimates.a_date = actuals.a_date
UNION ALL
SELECT *
FROM estimates
RIGHT JOIN actuals ON estimates.employee_id = actuals.employee_id
AND estimates.a_date = actuals.a_date
WHERE estimates.employee_id IS NULL
OR estimates.a_date IS NULL) AS qFullJoinEstimatesActuals
I have saved this query as an object (let's call it qEstimatesAndActuals). My objective is to LEFT JOIN qEstimatesAndActuals with another table. Something like the following:
SELECT *
FROM qJoinedTable
LEFT JOIN (SELECT *
FROM labor_rates) AS rates
ON qJoinedTable.employee_id = rates.employee_id
AND qJoinedTable.a_date BETWEEN rates.begin_date AND rates.end_date
MS Access accepts the syntax and runs the query, but it omits results that are clearly within the result set. Wondering if the date format was somehow lost, I placed a FORMAT around the begin_date and end_date to force them to be interpreted as Short Dates. Oddly, this produced a different result set, but it still omitted result that it shouldn't have.
I am wondering if the queries are performed in such a way that you can't LEFT JOIN the result set of a UNION ALL. Does anyone have any thoughts/ideas on this? Is there a better way of accomplishing the end goal?
I would try breaking each part of the query into its own access query object, e.g.
SELECT *
FROM estimates
LEFT JOIN actuals ON estimates.employee_id = actuals.employee_id
AND estimates.a_date = actuals.a_date
Would be qryOne
SELECT *
FROM estimates
RIGHT JOIN actuals ON estimates.employee_id = actuals.employee_id
AND estimates.a_date = actuals.a_date
WHERE estimates.employee_id IS NULL
OR estimates.a_date IS NULL
Would be qryTwo
SELECT * FROM qryOne
UNION ALL
SELECT * FROM qryTwo
Would be qryFullJoinEstimatesActuals, and finally
SELECT NZ(estimates.employee_id, actuals.employee_id) AS employee_id
, NZ(estimates.a_date, actuals.a_date) AS a_date
, estimates.estimated_hours
, actuals.actual_hours
FROM qryFullJoinEstimatesActuals
I've found that constructs that don't work in complex Access SQL statements often do work properly if they are broken down into individual query objects and reassembled step-by-step. Additionally, you can test each part of the query individually. This will help you find a workaround if one proves to be necessary.
You can find exactly how to do this here.
You're missing an INNER JOIN.... UNION ALL step.
Consistent with the odd behavior surrounding the dates, this issue turned out to be related to the use of NZ to select a date from qFullJoinEstimatesActuals. The use of NZ appears to make the data type ambiguous. As such, the following line from the example in my post caused the error:
, NZ(estimates.a_date, actuals.a_date) AS a_date
The ambiguous data type of a_date caused the BETWEEN operator to produce erroneous results when comparing a_date to rates.begin_date and rates.end_date in the LEFT JOIN. The issue was resolved by type casting the result of the NZ function, as follows:
, CDate(NZ(estimates.a_date, actuals.a_date)) AS a_date