SQL Server date range query not working as expected - sql

I wrote a query for return orders received for a certain date range. The goal is to find orders made in a few hours for online and store. However, the order return ORDER_IDs that are outside the date range. Here is my query.
SELECT
ORDER_ID
FROM
ORDERS
WHERE
TRANSACTION_TYPE = 'ONLINE' OR TRANSACTION_TYPE = 'STORE'
AND ORDER_COUNT > 0
AND (ORDER_DATE >= '2017-11-20 02:04:25.247'
AND ORDER_DATE< '2017-11-20 02:06:40.887')
Now when I run a query with one of the ORDER_ID returned, it returns a date range outside the lower date value.
SELECT ORDER_DATE FROM ORDERS WHERE ORDER_ID=104
ORDER_DATE
-----------
2017-11-24 10:10:14.520
This clearly is incorrect. Why is it returning a date greater than 2017-11-20 when the date range is supposed to be between 2017-11-20 02:04:25.247 and 2017-11-20 02:06:40.887?
How can I fix the query to find orders a few hours apart, for example orders made on the same day between 3pm and 8pm?
When I tried the suggestion I get no rows, even though there are rows in the date range.
ORDER_ID ORDER_DATE TRANSACTION_TYPE
433 2017-11-20 02:04:25.247 ONLINE
448 2017-11-20 02:06:40.887 ONLINE

your WHERE condition is wrong. Maybe should be like this
WHERE ( -- added this
TRANSACTION_TYPE = 'ONLINE'
OR TRANSACTION_TYPE = 'STORE'
) -- added this
AND ORDER_COUNT > 0
AND (
ORDER_DATE >= '2017-11-20 02:04:25.247'
AND ORDER_DATE < '2017-11-20 02:06:40.887'
)
you missed out some parenthesis ( ) somewhere

This worked:
WHERE(
TRANSACTION_TYPE = 'ONLINE'
OR
TRANSACTION_TYPE = 'STORE'
)
AND ORDER_COUNT > 0
AND
(
ORDER_DATE <CURRENT_TIMESTAMP
AND
ORDER_DATE > DATEADD(hh,-3,CURRENT_TIMESTAMP)
)

You've mixed AND's and OR's together. One solution is to use IN for TRANSACTION_TYPE instead of two statements with OR operator. This avoids mixing of AND and OR.
SELECT ORDER_ID
FROM ORDERS
WHERE TRANSACTION_TYPE IN ('ONLINE', 'STORE')
AND ORDER_COUNT>0
AND ORDER_DATE >= '2017-11-20 02:04:25.247'
AND ORDER_DATE< '2017-11-20 02:06:40.887'
Another possibility is to use parenthesis to group the OR statements.

Related

In SQL how do I select the latest date that does not have a zero value in another column

I am trying to select the max date in a table that has a Booking Date and a Written Premium value for that date. I want the newest date that has Written Premium (not equal to Zero).
In the above table I want, or expect the 4th Row in my query (7/28/2021, 330000), but I get the first row
(8/20/21, 0)
This is the query I run:
SELECT
MAX(booking_date) AS [Max Booking Date]
FROM
DW.dbo.Table1
GROUP BY
booking_year
HAVING
SUM(written_premium) <> 0
AND booking_year = '2021'
I think this is summing all the written premium, not over the rows so I am just getting the max booking date. Maybe I need a PARTITION BY function or something? I am stuck on this and would appreciate any help on how to best do this.
Thanks,
Brian
I think there are multiple options, but one could be:
SELECT TOP 1 booking_date, written_premium
FROM DW.dbo.Table1
WHERE written_premium <> 0
ORDER BY booking_date DESC
If all you want is the date then there is no need of group by and a HAVING clause.
Set your conditions in the WHERE clause:
SELECT MAX(booking_date) AS [Max Booking Date]
FROM DW.dbo.Table1
WHERE booking_year = '2021' AND written_premium <> 0;
If you want both columns:
SELECT TOP 1 booking_date AS [Max Booking Date], written_premium
FROM DW.dbo.Table1
WHERE booking_year = '2021' AND written_premium <> 0
ORDER BY booking_date DESC;

how to query a table date against a series of dates on another table

I have two tables, INVOICES and INV_PRICES. I am trying to find the Invoice table's part price from the Inv_Prices based upon the Invoice_Dt on the Invoice table; if the Invoice_Dt is between (greater than, but less than) or greater than the max EFF_DT on the Inv_Prices, then return that part's price.
I have tired variations on the following code, but no luck. I either do not get all the parts or multiple records.
SELECT DISTINCT A.INVOICE_NBR, A.INVOICE_DT, A.PART_NO,
CASE WHEN TRUNC(A.INVOICE_DT) >= TRUNC(B.EFF_DT) THEN B.DLR_NET_PRC_AM
WHEN (TRUNC(A.INVOICE_DT)||ROWNUM >= TRUNC(B.EFF_DT)||ROWNUM) AND (TRUNC(B.EFF_DT)||ROWNUM <= TRUNC(A.INVOICE_DT)||ROWNUM) THEN B.DLR_NET_PRC_AM
/*MAX(B.EFF_DT) THEN B.DLR_NET_PRC_AM*/
ELSE 0
END AS PRICE
FROM INVOICES A,
INV_PRICES B
WHERE A.PART_NO = B.PART_NO
ORDER BY A.INVOICE_NBR
Can someone assist? I have a sample of each table if needed.
Doesn't it work to put the condition in the JOIN conditions? You can calculate the period when a price is valid using LEAD():
SELECT i.INVOICE_NBR, i.INVOICE_DT, i.PART_NO,
COALESCE(ip.DLR_NET_PRC_AM, 0) as price
FROM INVOICES i LEFT JOIN
(SELECT ip.*, LEAD(eff_dt) OVER (PARTITION BY PART_NO ORDER BY eff_dt) as next_eff_dt
FROM INV_PRICES ip
) ip
ON i.PART_NO = ip.PART_NO AND
i.invoice_dt >= ip.eff_dt AND
(i.invoice_dt < ip.next_eff_dt or ip.next_eff_dt is null)
ORDER BY i.INVOICE_NBR

SQL: How to find the records with more than an hour between them

I have a MYOB database in SQL Server 2008 R2.
I have two tables, SALESORD_HDR and SALESORDHIST. For each record in SALESORD_HDR, there is several records in SALESORDHIST. I want to count the number of records (sasles orders) in the hdr table, that have more than an hour between the first instance of a record in the hist table that has status 'R', and the last record that has status 'I'. Here's some sample data:
Table headers:
SALESORD_HDR: SEQNO(pk, int), STATUS(varchar(1)), ORDERDATE(datetime)
SALESORDHIST: SEQNO(pk, int), HEADER_SOURCE_SEQNO(from alesord.seqno), EVENT_TYPE(varchar(1)), HISTDATETIME(Datetime)
Data in Salesordhist:
SEQNO HEADER_SOURCE_SEQ EVENT_TYPE HISTDATETIME
1069559 435015 N 01:15.0
1069560 435015 O 01:15.0
1069561 435015 O 01:15.0
1069562 435015 R 01:16.0---
1069563 435015 R 01:16.0
1069586 435015 I 02:24.0
1069587 435015 I 02:24.0---
Data in Salesord_hdr:
SEQNO STATUS ORDERDATE
435114 2 2014-01-29 00:00.0
So, I want to return the count of orders that have more that an hour between the two lines marked --- in their related salesordhist lines. The common identifier is the SEQNO in SALESORD_HDR is in HEADER_SOURCE_SEQNO in SALESORDHIST
I have a suspicion I need to create a temporary table to do this one, but I'm out of my league here.
Any guidance much appreciated.
SELECT SEQNO, COUNT(*)
FROM SALESORD_HDR NATURAL JOIN /* on SEQNO */
(SELECT SEQNO,
HEADER_SOURCE_SEQ,
MIN(CASE WHEN STATUS='R' THEN HISTDATETIME ELSE NULL END) AS RTIME,
MAX(CASE WHEN STATUS='I' THEN HISTDATETIME ELSE NULL END) AS ITIME)
FROM SALESORDHIST
GROUP BY SEQNO, HEADER_SOURCE_SEQ)
AS TIME_SUBQUERY
WHERE ITIME IS NOT NULL AND RTIME IS NOT NULL
AND datediff(hour, RTIME, ITIME) > 1 /* check your favorite DB's date arithmetic commands */
GROUP BY SEQNO;
I don't know your database system, so its INTERVAL notation or timestamp subtraction command may be different. And I am not sure I understand the relationship between the two tables and what you want counted. But the MIN and MAX trick should get you through without a temp table and with only one pass through SALESORDHIST.
I can think of other ways to do this if the large table is indexed; if there are a lot of records multiple queries per group might work better than the table scan I expect this to generate.
UPDATE: changed from Postgres INTERVAL date arithmetic to datediff
I assume that header_source_seq identifies an order. The following gets the count:
select count(*)
from (select header_source_seq,
min(case when event_type = 'R' then histdatetime end) as rdt,
max(case when event_type = 'I' then histdatetime end) as idt
from salesordhist soh
group by header_source_seq
) t
where idt >= rdt + 1/24.0;
The subquery finds the orders (header_source_seqs) with the associated times. The outer query simply counts them subject to the condition. In SQL Server, adding a number to a datetime is interpreting as adding that many days. An hour is 1/24.0 days.
EDIT:
For today's orders, we'll need to join in the header table after all:
select count(*)
from (select header_source_seq,
min(case when event_type = 'R' then histdatetime end) as rdt,
max(case when event_type = 'I' then histdatetime end) as idt
from salesordhist soh join
salesord_hdr hdr
on soh.header_source_seq = hdr.seqno
where hdr.orderdate >= cast(getdate() as date)
group by header_source_seq
) t
where idt >= rdt + 1/24.0;
Note that this uses the condition:
where hdr.orderdate >= cast(getdate() as date)
First, I'm assuming there are no future orders. If that's wrong, it is easily fixed. Second, the orderdate column is not in a function. This allows an index on the column to be used. Third, the time is truncated by converting to a date.

Using COUNT and UNION to extract data and support a scenario

I am dealing with a database with thousands of customers.
I am wanting to find groups of single customers who have exactly ONE qualifying discount voucher which is valid and exactly ONE non-qualifying voucher which is valid.
A qualifying voucher is one that has a minimum spend amount of £0.01 or more.
A non-qualifying voucher is one that does not have a minimum spend and is therefore £0.00
'Valid' refers to the 'from' date being today or before and the 'to' date being today or in the future
I have initially set up the query below but all this is doing is searching for all customers who have valid qualifying AND non-qualifying voucherS. I am trying to find customers who have JUST ONE valid qualifying voucher and JUST ONE non-qualifying voucher:
select CustomerId, VoucherId, MinimumSpendAmount, ValidFromDate, ValidToDate
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
order by CustomerId
I think I need to split this into 2 separate SELECT statements, one looking for single customers with 1 qualifying voucher (using COUNT), and one looking for single customers with 1 non-qualifying voucher (using COUNT). And then combining them with a UNION. But I could be totally wrong...
Please can anybody help
You can use a sub select with a GROUP BY and HAVING CLAUSE to find the customers that match your criteria.
select CustomerId, VoucherId, MinimumSpendAmount, ValidFromDate, ValidToDate
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
and CustomerId in
(select CustomerId
from dbo.discountvoucher
where ValidFromDate <= 15/11/2013
and ValidToDate >= 15/11/2013
group by CustomerId
having sum(case when MinimumSpendAmount > 0 then 1 else 0 end) = 1
and sum(case when MinimumSpendAmount = 0 then 1 else 0 end) = 1
)
order by CustomerId

In Oracle SQL, how do you query the proportion of records of a certain value?

Say, you have a query like
SELECT COUNT(*), date FROM ORDERS GROUP BY date ORDER BY date
but you also want to have a third "phantom/dummy field", where it basically tells you the fraction of orders each day that are of a particular type (lets say "Utensils" and "Perishables").
I should say that there is an additional column in the ORDERS table that has the type of the order:
order_type
The third dummy column should do something like take the count of orders on a date that have the "Utensils" or the "Perishables" type (not XOR), then divide by the total count of orders of that day, and then round to 2 decimal points, and append a percentage sign.
The last few formatting things, aren't really important...all I really need to know is how to apply the logic in valid PLSQL syntax.
Example output
4030 2012-02-02 34.43%
4953 2012-02-03 16.66%
You can do something like
SELECT COUNT(*),
dt,
round( SUM( CASE WHEN order_type = 'Utensils'
THEN 1
ELSE 0
END) * 100 / COUNT(*),2) fraction_of_utensils_orders
FROM ORDERS
GROUP BY dt
ORDER BY st
If you find it easier to follow, you could also
SELECT COUNT(*),
dt,
round( COUNT( CASE WHEN order_type = 'Utensils'
THEN 1
ELSE NULL
END) * 100/ COUNT(*), 2) fraction_of_utensils_orders
FROM ORDERS
GROUP BY dt
ORDER BY st
To Add sum of orders of same type to query:
select
o.*,
(
select count(o2.OrderType)
from ORDERS o2
where o2.OrderType = o.OrderType
) as NumberOfOrdersOfThisType
from ORDERS o
To Add fraction of orders of same type to query:
(Check variable definition to make sure it is PL/SQL)
declare totalCount number
select count(*)
into totalCount
from ORDERS
select
o.*,
(
select count(o2.OrderType)
from ORDERS o2
where o2.OrderType = o.OrderType
) / totalCount as FractionOfOrdersOfThisType
from ORDERS o