PostgreSQL - What is the easiest way to convert seconds in a numeric field to MM:SS - datetime

I have field with an integer value which means number of seconds. I would want to display it as MM:SS, without hours, so for example 6000 seconds should be shown as 100:00.
What's the easiest way to do this? As far as I see, to_char cannot be used for this.

This is a kind of workaround, as I haven't found better solution:
You could use a simple select:
test=# select (6022/60)::TEXT || ':' || (6022%60)::TEXT;
?column?
----------
100:22
(1 row)
This could be wrapped in a nice function:
CREATE FUNCTION format(integer) RETURNS TEXT AS $$
select ($1/60)::TEXT || ':' || ($1%60)::TEXT;
$$ LANGUAGE SQL STABLE;
Example of usage:
test=# SELECT format(6);
format
--------
0:6
(1 row)
test=# SELECT format(60);
format
--------
1:0
(1 row)
test=# SELECT format(600);
format
--------
10:0
(1 row)
test=# SELECT format(6000);
format
--------
100:0
(1 row)

Related

How can I fix wrong date format in SQLite

I'm working on app where I use SQLite to store data.
I created column Date. Since I'm beginner I made a mistake by inputing date as %m/%d/%Y (for example: 2/20/2020)
Now I've got a problem while taking out rows between selected dates.
I tried using this code:
SELECT * FROM Table WHERE Date BETWEEN strftime('%m/%d/%Y','2/5/2019') AND strftime('%m/%d/%Y','2/20/2020')
But that's not working.
Example table:
ID | Date
01 | 9/2/2019
02 | 2/20/2020
Thank you in advance for your help.
Update your dates to the only valid for SQLite date format which is YYYY-MM-DD:
update tablename
set date = substr(date, -4) || '-' ||
substr('00' || (date + 0), -2, 2) || '-' ||
substr('00' || (substr(date, instr(date, '/') + 1) + 0), -2, 2);
See the demo.
Results:
| ID | Date |
| --- | ---------- |
| 1 | 2019-09-02 |
| 2 | 2020-02-20 |
Now you can set the conditions like:
Date BETWEEN '2019-02-05' AND '2020-02-20'
If you do this change then you can use the function strftime() in select statements to return the dates in any format that you want:
SELECT strftime('%m/%d/%Y', date) date FROM Table
If you don't change the format of date column then every time you need to compare dates you will have to transform the value with the expression used in the UPDATE statement, and this is the worst choice that you could make.

trying to understand how oracle REGEXP_REPLACE work

Need help being new to "REGEXP_REPLACE".
When I do
SELECT REGEXP_REPLACE('7ELEVEN USA','[(\D^USA|^CANADA|^Canada)]','') "NAME" from dual
I get 7ELEVE and you can see that last character N is missing.
I want to replace first numbers from below & display 7-ELEVEN STORE.
20991 7-ELEVEN STORE
Any help is greatly appreciated.
Thanking in advance
I want to replace first numbers from below & display 7-ELEVEN STORE.
20991 7-ELEVEN STORE
Well, you don't even need regular expressions for that - the good, old SUBSTR + INSTR does the job just fine (that's RES2). If you want regexp, then this pattern: ^\d+ does it - it says:
^ anchor to the beginning of the string
\d+ take all digits there are (up to the first non-digit character, which is the space)
An example:
SQL> with test (col) as
2 (select '20991 7-ELEVEN STORE' from dual)
3 select
4 regexp_replace(col, '^\d+') res1,
5 substr(col, instr(col, ' ') + 1) res2
6 from test;
RES1 RES2
--------------- --------------
7-ELEVEN STORE 7-ELEVEN STORE
SQL>
[EDIT]
As of the first query you posted (I didn't understand it was the question): if you want to select the first "word" from that string, I wouldn't use REGEXP_REPLACE but (REGEXP_)SUBSTR:
SQL> with test (col) as
2 (select '7ELEVEN USA' from dual)
3 select regexp_substr(col, '\w+') res1,
4 substr(col, 1, instr(col, ' ') - 1) res2
5 from test;
RES1 RES2
------- -------
7ELEVEN 7ELEVEN
SQL>

How to format a Float number in SQLite?

In SQLite I need to format a number to display it with the thousand separator and decimal separator. Example: The number 123456789 should be displayed as 1,234,567.89
What I did partially works because it does not display the thousand separator as I expected:
select *, printf ("U$%.2f", CAST(unit_val AS FLOAT) / 100) AS u_val FROM items;
u_val shows: U$1234567.89 but I need U$1,234,567.89
The following is one way that you could accomplish the result:-
select *, printf ("U$%.2f", CAST(unit_val AS FLOAT) / 100) AS u_val FROM items;
Could become :-
SELECT
*,
CASE
WHEN len < 9 THEN myfloat
WHEN len> 8 AND len < 12 THEN substr(myfloat,1,len - 6)||','||substr(myfloat,len - 5)
WHEN len > 11 AND len < 15 THEN substr(myfloat,1,len -9)||','||substr(myfloat,len-8,3)||','||substr(myfloat,len-5)
WHEN len > 14 AND len < 18 THEN substr(myfloat,1,len - 12)||','||substr(myfloat,len -11,3)||','||substr(myfloat,len-8,3)||','||substr(myfloat,len-5)
END AS u_val
FROM
(
SELECT *, length(myfloat) AS len
FROM
(
SELECT *,printf("U$%.2f",CAST(unit_val AS FLOAT) / 100)) AS myfloat
FROM Items
)
)
The innermost SELECT extracts the original data plus a new column as per your orginal SELECT.
The intermediate SELECT adds another column for the length of the new column as derived from unit_val via the printf (this could have been done in the first/innermost SELECT, getting this value simplifies (in my opinion) the outermost SELECT, or you could use multiple length(myfloats) in the outermost SELECT).
RESULT - Example
The following is the result from a testing (source column is myfloat) :-
The highlighted columns being the original columns.
The circled data being the result.
The other 2 columns are intermediate columns.
Edit
As you've clarified that the input is an integer, then :-
SELECT *,'U$'||printf('%,d',(unit_val/100))||'.'||CAST((unit_val % 100) AS INTEGER) AS u_val FROM Items
would work assuming that you are using at least version 3.18 of SQLite.
Correction
Using the SQL immediately above if the value of the last part (the cents) is less than 10 then the leading 0 is dropped. So the correct SQL is. Note for simplicity the CAST has also been dropped and rather than concatening the . it has been added to the printf formatter string so :-
SELECT
'U$' ||
printf('%,d', (unit_val / 100)) ||
printf('.%02d',unit_val % 100)
AS u_val
FROM Items
Or as a single line
SELECT 'U$' || printf('%,d', (unit_val / 100)) || printf('.%02d',unit_val % 100) AS u_val FROM Items
Here is a suggestion:
WITH cte AS (SELECT 123456789 AS unit_val)
SELECT printf('%,d.%02d', unit_val/100, unit_val%100) FROM cte;
The Common Table Expression is just there to supply a dummy value, in the absence of variables.
The %,d format adds thousands separators, but, as many have pointed out, only for integers. Because of that, you will need to use the unit_val twice, once for the integer part, and again to calculate the decimal part.
SQLite truncates integer division, so unit_val/100 gives you your dollar part. The % operator is a remainder operator (not strictly the same as “mod”), so unit_val%100 gives the cents part, as another integer. The %02d format ensures that this is always 2 digits, padding with zeroes if necessary.

Recursively add to a data table in SAS

I am new to SAS. I need to do x-iterations to populate my dataset called MYRS.
Each iteration needs to JOIN TABLE1 with (TABLE2+ MYRS) MINUS the records which are already in MYRS table.
Then, I need to update MYRS table with additional matches. The GOAL is to track a chain of emails.
MYRS is essentially a copy of TABLE1 and contains matching records. Kind of tricky. (simplified schema). Table1 Can have DUPS.
For example
TABLE1:
ID | EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4|
1 | A | s | d | F
2 | g | F | j | L
3 | z | x | L | v
4 | z | x | L | v
2 | g | F | j | L
TABLE2:
EMAIL
A
MYRS (starts as empty dataset)
EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4
Logic: TABLE1 has email that matches email in TABLE2. Therefore this record need to show up. Other records don't match anything in TABLE2. But because Record1 and Record2 share the same ALTERNATIVE email F, Record2 also need to be shown. But because Record2 and Record3 share same alternative email L, Record3 also needs to be shown. And so fourth...
proc sql;
SELECT TABLE1.id,
TABLE1.email1,
TABLE1.email2,
TABLE1.email3,
TABLE1.email4
FROM TABLE1
INNER JOIN (
SELECT EMAIL
FROM TABLE2
UNION
SELECT EMAIL1 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL2 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL3 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL4 AS EMAIL
FROM MYRS
)
ON EMAIL=EMAIL1 OR EMAIL=EMAIL2 OR EMAIL=EMAIL3 OR EMAIL=EMAIL4
WHERE TABLE1.id NOT IN (
SELECT DISTINCT ID
FROM MYRS
)
quit;
How can I create the following logic:
Wrap this into some sort of function
Before sql execution, count amount of records in MYDS and SAVE the count
Execute SQL and update MYDS
Count amount of records in MYDS
If MYDS count did not change, stop execution
Else, goto #3
I am very new to SAS (3 days to be exact) and trying to put everything together. (I would use the logic above if I was to do that in Java)
Here is a macro approach, it mostly follows your logic but transforms your data first and the input/output is a list of IDs (you can easily get to and from emails with this).
This code will probably introduce quite a few SAS features that you are unfamiliar with, but the comments and explanations below should help . If any of it is still unclear take a look at the links or add a comment.
It expects input data:
inData: Your TABLE1 with ID and EMAIL* variables
matched: An initial list of known wanted IDs
It returns:
matched: An updated list of wanted IDs
/* Wrap the processing in a macro so that we can use a %do loop */
%macro looper(maxIter = 5);
/* Put all the emails in one column to make comparison simpler */
proc transpose data = inData out = trans (rename = (col1 = email));
by ID;
var email:;
run;
/* Initialise the counts for the %where condition */
%let _nMatched = 0;
%let nMatched = 1;
%let i = 0;
/* Loop until no new IDs are added (or maximum number of iterations) */
%do %while(&_nMatched. < &nMatched. and &i < &maxIter.);
%let _nMatched = &nMatched.;
%let i = %eval(&i. + 1);
%put NOTE: Loop &i.: &nMatched. matched.;
/* Move matches to a temporary table */
proc datasets library = work nolist nowarn;
delete _matched;
change matched = _matched;
quit;
/* Get new matched IDs */
proc sql noprint;
create table matched as
select distinct c.ID
from _matched as a
left join trans as b
on a.ID = b.ID
left join trans as c
on b.email = c.email;
/* Get new count */
select count(*) into :nMatched from matched;
quit;
%end;
%mend looper;
%looper(maxIter = 10);
The interesting bits are:
proc transpose: Converts the input into a deep table so that all the email addresses are in one variable, this makes writing the email comparison logic simpler (less repetition needed) and puts the data in a format that will make it easier for you to clean the email addresses if necessary (think upcase(), strip(), etc.).
%macro %mend: The statements used to define a macro. This is necessary as you cannot use macro logic or loops in open code. I've also added an argument so you can see how that works.
%let and select into :: Two ways to create macro variables. Macro variables are referenced with the prefix & and are used to insert text into the SAS program before it is executed.
%do %while() %end: One of the ways to perform a loop within a macro. The code within will be run repeatedly until the condition evaluates to false.
proc datasets: A procedure for performing admin tasks on datasets and libraries. Used here to delete and rename temporary tables.

How do I return date with milliseconds

I'm struggling to return a date with milliseconds...
I've tried
my_date::datetime
and
my_date::timestamp
but both only return to the second?
Surely there's a way of doing this? Documentation isn't too helpful!
Thanks
The my_date column also has to contain the millisecond component (I'm assuming that it's of a timestamp type). Have you verified that it does?
dbadmin=> CREATE TABLE date_test (my_date timestamp);
CREATE TABLE
dbadmin=> INSERT INTO date_test (my_date) VALUES (SYSDATE());
OUTPUT
--------
1
(1 row)
dbadmin=> INSERT INTO date_test (my_date) VALUES ('2015-01-30 09:43:41');
OUTPUT
--------
1
(1 row)
dbadmin=> COMMIT;
COMMIT
dbadmin=> SELECT my_date::timestamp FROM date_test;
my_date
----------------------------
2015-01-30 09:46:48.746415
2015-01-30 09:43:41
(2 rows)

Resources