How to increase the chance that an old quote is selected - sql

I have a database which contains quotes. I like to display random quotes. But with two conditions.
Quotes that are displayed the last week should not be selected.
How farther in the past a quote is displayed, the more chance it has to be to be selected.
The first is not so difficult. I can use the WHERE clause for this.
The second I do not know how to implement. Is there an easy way to do this, or need I to define a complex function for this?
One extension that could be nice also. Initially most quotes will not have been displayed. If it would be possible to give a quote that has not been displayed yet a much bigger chance to be displayed, then it would be quite nice.

A possible approach: order by the length of time in the past the quote was used, times a random number. This has the effect of giving a greater weight to records further in the past.
Here is pseudo code:
select quote from quotes
where current date - display date > 1 week
order by ((current date - display date - 1 week) * random number) desc
fetch first row only
You will have to adapt it to your system's date/time functions, since these are highly variable.
You probably need to use an intermediate step to apply a random number to each row, because directly ordering by rand() doesn't work on many systems.
Update: Here is a working MySQL example.

Related

Conversion failing for date formatting

I have an nvarchar(100) column which has a value ' 8/11/2022'.
I receive and error when trying to convert it to date...
select convert(date,[date],103)
from [Source].[TableName] s_p
--Msg 241, Level 16, State 1, Line 96
--Conversion failed when converting date and/or time from character string.
I have tried a number of different ways to approach but I can't find one to give me '08/11/2022'
select Date = REPLACE(LEFT([Date],10),' ','0')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select REPLACE([DATE],' 8/','08/')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select convert(nvarchar,[date],103)
from [Source].[TableName] s_p
--Outcome 8/11/2022
The strange thing is when I copy and paste from the results grid then do a replace it works fine...
select REPLACE(' 8/11/2022',' 8/','08/')
--Outcome 08/11/2022
Please help me to get to '08/11/2022' or any single digit to having a leading 0.
Thanks, Will
Different languages and cultures have their own formatting preferences around date values. Some places like M/dd/yyyy. Some places like dd/MM/yyyy. Or perhaps d-M-YYYY (different separators and conventions around leading zeros). The point is it's not okay to go into a place and impose our own preferences and norms on that culture.
The SQL language is no different. It really is it's own language, and as such has it's own expectations around date handling. If you violate these expectations, you should not be surprised when there are misunderstandings as a result.
The first expectation is for date and datetime values to be stored in datetime columns. It's hard to understate how much of a difference this can make for performance and correctness.
But let's assume that's not an option, and you have no choice but to use a string column like varchar or nvarchar. In that situation, there is still an expectation around how date values should be formatted.
Any database will do better if you use a format which stores the date parts in order by descending length. For example, ISO-8601 yyyy-MM-ddTHH:mm:sss[.fff] This is important to allow greater than/less than comparisons to work, it can greatly help with indexes and performance, and it makes cast/convert operations to datetime values MUCH more likely to succeed and be accurate.
For SQL Server specifically, there are three acceptable formats:
yyyy-MM-ddTHH:mm:sss[.fff],
yyyyMMdd HH:mm:ss[.fff], and
yyyyMMdd.
Anything else WILL have date values that don't parse as expected. Any string manipulation done to call the CONVERT() method should focus on reaching one of these formats.
With that in mind, and assuming 8/11/2022 means November 8 and not August 11 (given the 103 convert format), you need something like this:
convert(datetime,
right([date], charindex('/', reverse([date]))-1) -- year
+ right('0' + replace(substring([date], charindex('/', [date])+1, 2), '/', ''), 2) -- month
+ right('0' + left([date], charindex('/',[date])-1),2) -- day
)
And you can see it work here:
https://dbfiddle.uk/lM8sVySh
Yes, that's a lot of code. It's also gonna be more than a little slow. And again, the reason why it's so slow and complicated is you jumped in with your own cultural expectations and failed to respect the language of the platform you're using.
Finally, I need to question the premise. As the fiddle above shows, SQL Server is perfectly happy to convert this specific value without error. This tells me you probably have more rows, and any error is in fact coming from a different row.
With that in mind, one thing to remember is a WHERE clause condition will not necessarily run or filter a table before a CONVERT() operation in the SELECT clause. That is, if you have many different kinds of value in this table, you cannot guarantee your CONVERT() expression will only run on the date values, no matter what kind of WHERE clause you have. Databases do not guarantee order of operations in this way.
The problem could also be some invisible unicode whitespace.
Another possibility is date formats. Most cultures that prefer a leading day, instead of month or year, tend to also strongly prefer to see the leading 0 in the first place. That the zero is missing here makes me wonder if you might have a number of dates in the column that were formatted by, say, Americans. So then you try to parse a column with values both like 02/13/2022 and 13/02/2022. Obviously those dates can't both use the same format, since there is no 13th month.
In that case, best of luck to you, because you no longer have any way to know for certain whether 2/3/2022 means March 2nd or February 3rd... and trying to guess (by say, assuming your own common format) is just exacerbating the same mistake that got you into this mess in the first place.
It's worth noting all three of these possibilities would be avoided had you used DateTime columns from the beginning.
You'll want to use LPAD to add 0 to string, then CAST() string as date if you want to change to date data type

How to use REGEXP_LIKE() for concatenation in Oracle

I need to make some changes in SQL within a CURSOR. Previously, the maximum value for column 'code' was 4 characters (e.g. K100, K101,....K999) but now it needs to be 8 characters (e.g. K1000, K1001, K1002,....K1000000).
CURSOR c_code(i_prefix VARCHAR2)
IS
SELECT NVL(MAX(SUBSTR(code,2))+1,100) code
FROM users
WHERE code LIKE i_prefix||'___';
The 'code' column value starts from 100 and increments +1 each time a new record is inserted. Currently, the maximum value is 'K999' and I would like it to be K1000, K1001, K1002 and so on.
I have altered and modified the 'code' column to VARCHAR(8) in the users table.
Note: i_prefix value is always 'K'.
I have tried to amend the SQL -
CURSOR c_code(i_prefix VARCHAR2)
IS
SELECT NVL(MAX(SUBSTR(code,2))+1,100) code
FROM users
WHERE code LIKE i_prefix||'________';
However, it restarts from 100 and not from K1000, K1001, K1002, etc. each time a record is inserted.
I have been suggested to use REGEXP_LIKE() but not sure how to properly use it to get the desired outcome in this case.
Can you please guide me on how can we get this result using REGEXP_LIKE().
Thank you.
Your old code
WHERE code LIKE i_prefix||'___';
will match K followed by exactly three characters, which is what you had. Your new code
WHERE code LIKE i_prefix||'________';
will match K followed by exactly eight characters, which is one too many for a start, since you said the total length was eigh - which means you need sever wilcard placeholders:
WHERE code LIKE i_prefix||'_______';
... but that still won't work at the moment since your existing values aren't that long. As all your current values are at least four, you could do:
WHERE code LIKE i_prefix||'___%';
which will match K followed by three or more characters - with no upper limit, but your column is restricted to eight too anyway.
If you did want to use a regular expression, which are generally slower, you could do:
WHERE REGEXP_LIKE(code, i_prefix||'.{3,7}');
which would match K followed by three to seven characters, or:
WHERE REGEXP_LIKE(code, i_prefix||'\d{3,7}');
which would only match K followed by three to seven digits.
fiddle
However, I would suggest you use a sequence to generate the numeric part, and just prefix that with the K character. The sequence could start from 100 on a new system with no data, or from the current maximum number in an existing system with data.
I would also consider zero-padding the data, including all the existing values, to allow them to be compared; so update K100 to K0000100. Or if you can't do that, once you get past K199 jump to K2000000. Either would then allow the values to be sorted easily as strings. Or, perhaps, add a virtual column that extracts the numeric part as a number.

date handling in sqlite3 confusing.

I am from non database background. I have created a table with one of the field data type TEXT.
dataF TEXT
INSERTION:
Have inserted three records with the values :
'842-2-4'
'842-2-5'
'842-2-6'
SELECTION:
Tring to get the records based on date now.
... where dateF between '842-2-4' and '842-2-10'
It fails.
Whereas,
... where dateF between '842-2-4' and '842-2-8'
retrieves all the 3 records.
What am i doing wrong ? Why the first statment fails ?
Kindly suggest.
Because you are comparing strings not dates. The computer has no idea these are dates. You have to either store as date and do a date comparison or implement your own logic to analyze strings.
Simply put, it is looking at the 1 in 10 as being less than your values rather than 10 being more. It's string comparison, not date.
Although sqlite doesn't support date types, it does have functions for dealing with them. See here:
http://www.sqlite.org/lang_datefunc.html
When comparing strings, the values are compared left to right...
As one string is shorter that the other, you are kind of comparing this...
'842-2-4'
'842-2-1'
Well, nothing is >= '842-2-4' AND <= '842-2-1'.
Because '842-2-1' comes before '842-2-4'.
And, so, '842-2-10' comes before '842-2-4' too.
Just as 'Squiggled' comes before 'Squiggly'
And as 'xxx-y-az' comes before 'xxx-y-z'
To compare as you desire, make sure all your dates are padded with 0's.
BETWEEN '0842-02-04' AND '0842-02-10'
But that will only work after you have padded out the values in your table too.
EDIT:
Also, note that this assumes that your format is YYYY-MM-DD. As a string is compared left to right, you must have the highest magnitude values to the left.
(This means that you can't use YYYY-DD-MM and still have native string comparisons behave as you would want them.)

RIGHT function, not returning whats expected?

Query:
SELECT StartDate, EndDate, RIGHT(Sector, 1 )
FROM Table1
ORDER BY Right(Sector, 1), StartDate
By looking at this, the query should order everything by sector, followed by the start date. This query has worked for quiet awhile until yesterday where it did not order it properly, for some reason, Sector 2 came before Sector 1.
The data type for Sector is of type int, not null. After inserting a TRIM function into Sector it seems to work fine afterwards.
New Query:
SELECT StartDate, EndDate, RIGHT(Sector, 1 )
FROM Table1
ORDER BY Right(TRIM(Sector), 1), StartDate
Which I found really weird since it's suppose to only pick out one character, so why is there leading spaces?
Is there an issue with using RIGHT function on a int before converting the type? Or is it something else?
Thanks for the help everyone!
-Edit- The RIGHT function should return either 1,2,3 or 4 however when ordering it, 2 comes before 1.
To clarify, the column Sector contains an int value, we can determine it's location by obtaining the last digit (Which is why the previous coder did)
MS Access 2003 has a curious little feature (I can't speak for the other versions):
Make a simple query. Sort by Column A Ascending. Save the query.
Run the query. When you see the output, sort by Column A Descending using the toolbar option (see pic below). Save & close.
Run the query again. Your new sort will have overridden the sort that you saved in the query.
I think you or someone else probably just opened the query out of curiosity, sorted by Sector Descending, and when prompted to save Design Changes, you chose Yes (even though technically you didn't make any). The easiest way I found to restore the original sort is to edit the query and save it.
You've got your data stored wrong if you need to sort on a subcharacter of a numeric field.
That said, in certain context, VBA functions reserve a space in string representations of numbers for the sign. A nonsensical example of this would be:
?Len("12345")
5
Notice the space at the beginning (where the - would be if the number returned by Len() could be negative). I thought this was a result of coercing a number to a string value, but that's not it, and I couldn't replicate the problem. But that would likely be the source of the problem, and, of course, trimming off the leading space would take care of the issue.
But that's two function calls for each line, and then you're sorting by it, and that means no use of indexes, so it's going to be slow relative to a SORT BY that can use indexes. So, I'd conclude you have a schema error, in that you're giving meaning to a subpart of the data stored in the field.
It seems pretty obvious that you have a blank space at the end of the Sector field that the trim is removing.

Input mask text box issue

There is a problem in VBA text box while filling input mask property:
I am trying to make the combination of date and time:
Hence i put it like below:
00/00/00;0;_00:00;0;_
But while running the application, i am only getting 00/00/00 (Date).
But i remember, i got the result as like 00/00/00 00.00 as expected when i first put the expression as like above;
but now i am not getting it :-(
The InputMask property can contain up to three sections separated by semicolons (;)
Your mask should be like this:
"00/00/00 00:00;0;0"
or
"00/00/00 00:00;0;_" // to display it like __/__/__ __:__
Why not just use the built in "General Date" format? I've found over the years that input masks are very restricting and basically a pain. Although it's been so long since I've used them that I don't recall the details of why I despise them.
This also has the benefit of respecting the users choices of regional date format. For example I always use yyyy-mm-dd format.
Also a client had a situation where the date format was decreed to be Medium Date on all fields. Which is dd-mmm-yy. It later turned out that in a table of 100K records there were twelve dates before 1900. They had simple had something extra keyed in in the year so Windows/Access interpreted those dates as being in the 3rd or 5th century or whatever. Now these dates weren't used in any kind of calculation so it wasn't a big deal. SQL Server upsizing to small date/time fields didn't appreciate those though.