How to use substring of result in PosrgreSQL/SQL - sql

I don't know how to name this syntax properly. I got a table (say T) with column A in which store the entries in format USER-YYYY-MMDD. I want to extract all rows whose A column's year part (YYYY part) is greater than 2010. E.g.
TABLE T
+----------------+
| A |
+----------------+
| USER-2011-1234*|
| USER-1992-1923 |
| USER-2014-1234*|
+----------------+
(*) are what I want: YYYY part is greater than 2010. SQL should looks like this, but I dont know how to say it in PostgreSQL.
SELECT * FROM T WHERE A[5-8] > 2010
Thanks!

select *
from t
where to_number(substr(a, 6, 4)) > 2010;
Note that his will fail with an error if the string cannot be converted to a number
More details in the manual: http://www.postgresql.org/docs/current/static/functions-string.html
Btw: storing more than one information in a single column is a bad design. You should store the username and the date in two different columns. Additionally storing dates as varchar is also a very bad idea. The date should be stored as date not as varchar

Try Like this
select *
from t
where substring(a::text from 6 for 4)::integer > 2010;

Related

Presto SQL: placeholder for "date" that must be greater than any other date

I have a query in Presto where I need to select some values based on date references of another table, structured like this:
ID | from | to
------+------------+-----------
A | - |
------+------------+-----------
B | 2020-11-01 |
------+------------+-----------
C | - | 2020-12-31
What this table means is that item A is still valid, item B is valid only since 2020-11-01 and item C is valid only until 2020-12-31.
The query should work like this
SELECT whatever
FROM table t
WHERE any_date BETWEEN t.from AND t.to
Now, in Presto I've discovered that any date is greater than -. So if I do SELECT '-' < '1930-01-01', it would return true.
This is good for me because there are some items that don't have an actual "From" value, so - is a good placeholder.
However, I haven't found a character / placeholder / wildcard / whatever that is always greater than any specific date.
Does something like this exist? If not, can you suggest a smart solution for this predicament, that doesn't require me adding fake dates like '2500-01-01' for those items that don't have a "to" field?

Query sql pivot on date transformation

I have a table called LAB and it has these important field:
DATA, CODE, STRUTT.
DATA contains dates, code string and strutt integer as well
What I need is to query this table and get back something like:
-----------------------
| DAYS | YEAR | YEAR2 |
-----------------------
| | STRUTT |
-----------------------
| | | |
They suggest me to use pivot. But I don't know how to group years.
My first try was:
SELECT * FROM
(
SELECT STRUTT, CODe, DATA
FROM LAB
WHERE DATA >= DATE '2016-05-01' AND DATA <= DATE '2016-05-31'
)
PIVOT
(
count(DISTINCT CODE)
FOR STRUTT IN (21,22,23,24,25,26)
)
This is a start for my use of Pivot. But then i tried to change the for statement and put:
FOR DATA IN ('2016-05-01','2016-05-21')
but this gave me errors. Some ideas for solve the entire problem? Or at least to solve the last one of dates? SOLVED BUT NOT NEEDED AT THE END TO SOLVE THE PROBLEM
It looks like you are using Oracle. I might suggest trying:
FOR DATA IN (DATE '2016-05-01', DATE '2016-05-21')
Otherwise, the string values are interpreted as strings, not dates.
I solve the problem by having a look somewhere... I still don't know which version of Oracle I'm using. By the way this work for me:
select strutt
, to_char(data,'dd-MON') AS dayMonth
, count(DISTINCT (case extract(year from data) when 2015 then code end)) numutenze_2015
, count(DISTINCT (case extract(year from data) when 2016 then code end)) numutenze_2016
from lab
where data >= DATE '2015-01-01' and data <= DATE '2016-06-08'
group by strutt, to_char(data,'dd-MON')
order by strutt, to_char(data,'dd-MON') asc
The only problem I have, I don't know how to order the Dates... it start from April, then August... ecc

Is there an established pattern for SQL queries which group by a range?

I've seen a lot of questions on SO concerning how to group data by a range in a SQL query.
The exact scenarios vary, but the general underlying problem in each is to group by a range of values rather than each discrete value in the GROUP BY column. In other words, to group by a less precise granularity than you're storing in the database table.
This crops up often in the real world when producing things like histograms, calendar representations, pivot tables and other bespoke reporting outputs.
Some example data (tables unrelated):
| OrderHistory | | Staff |
--------------------------- ------------------------
| Date | Quantity | | Age | Name |
--------------------------- ------------------------
|01-Jul-2012 | 2 | | 19 | Barry |
|02-Jul-2012 | 5 | | 53 | Nigel |
|08-Jul-2012 | 1 | | 29 | Donna |
|10-Jul-2012 | 3 | | 26 | James |
|14-Jul-2012 | 4 | | 44 | Helen |
|17-Jul-2012 | 2 | | 49 | Wendy |
|28-Jul-2012 | 6 | | 62 | Terry |
--------------------------- ------------------------
Now let's say we want to use the Date column of the OrderHistory table to group by weeks, i.e. 7-day ranges. Or perhaps group the Staff into 10-year age ranges:
| Week | QtyCount | | AgeGroup | NameCount |
-------------------------------- -------------------------
|01-Jul to 07-Jul | 7 | | 10-19 | 1 |
|08-Jul to 14-Jul | 8 | | 20-29 | 2 |
|15-Jul to 21-Jul | 2 | | 30-39 | 0 |
|22-Jul to 28-Jul | 6 | | 40-49 | 2 |
-------------------------------- | 50-59 | 1 |
| 60-69 | 1 |
-------------------------
GROUP BY Date and GROUP BY Age on their own won't do it.
The most common answers I see (none of which are consistently voted "correct") are to use one or more of:
a bunch of CASE statements, one per grouping
a bunch of UNION queries, with a different WHERE clause per grouping
as I'm working with SQL Server, PIVOT() and UNPIVOT()
a two-stage query using a sub-select, temp table or View construct
Is there an established generic pattern for dealing with such queries?
You can use some of the dimensional modeling techniques, such as fact tables and dimension tables. Order History can act as a fact table with DateKey foreign key relation to a Date dimension.
Date dimension can have a schema such as below:
Note that Date table is pre-filled with data up-to N number of years.
Using an example above, here is a sample query to get the result:
select CalendarWeek, sum(Quantity)
from OrderHistory a
join DimDate b
on a.DateKey = b.DateKey
group by CalendarWeek
For Staff table, you can store Birthday Key instead of age and let the query calculate the age and ranges.
Here is SQL Fiddle
Date dimension population script was taken from here.
As is often the case this SQL problem requires using more than one pattern in composition.
In this case the two you can use are
NTILE
Numbers Table
You can use NTITLE to create a set number of groups. However since you don't have each member of the groups represented you also need to use a numbers table Since you're using SQL Server you have it easy as you don't have to simulate either.
Here's an example for the Staff problem
WITH g as (
SELECT
NTILE(6) OVER (ORDER BY number) grp,
NUMBER
FROM
master..spt_values
WHERE
TYPE = 'P'
and number >=10 and number <=69
)
SELECT
CAST(min(g.number) as varchar) + ' - ' +
CAST(max(g.number) as varchar) AgeGroup ,
COUNT(s.age) NameCount
FROM
g
LEFT JOIN Staff s
ON g.NUMBER = s.Age
GROUP BY
grp
DEMO
You can apply this to dates as well it just requires some date to day maniplulation
Take a look at the OVER clause and its associated clauses: PARTITION BY, ROW, RANGE...
Determines the partitioning and ordering of a rowset before the
associated window function is applied. That is, the OVER clause
defines a window or user-specified set of rows within a query result
set. A window function then computes a value for each row in the
window. You can use the OVER clause with functions to compute
aggregated values such as moving averages, cumulative aggregates,
running totals, or a top N per group results.
My favorite case in this genre is where transactions must be grouped by fiscal quarter or fiscal year. The fiscal quarter or fiscal year boundaries of various enterprises can border on the bizarre.
My favorite way to implement this is to create a separate table for the attributes of a date. Let's call the table "Almanac". One of the columns in this table is the fiscal quarter, and another one is the fiscal year. The key to this table is of course the date. Ten years worth of data fill up 3,650 rows, plus a few for leap years. You then need a program that can populate this table from scratch. All the enterprise calendar rules are built into this one program.
When you need to group transaction data by fiscal quarter, you just join with this table over date, and then group by fiscal quarter.
I figure this pattern could be extended to groupings by other kinds of ranges, but I've never done it myself.
In your first example your intervals are regular so you can achieve the desired result simply by using functions. Below is an example that gets the data as you require it. The first query keeps the first column in date format (how I would preferably deal with it doing any formatting outside of SQL), the second does the string conversion for you.
DECLARE #OrderHistory TABLE (Date DATE, Quantity INT)
INSERT #OrderHistory VALUES
('20120701', 2), ('20120702', 5), ('20120708', 1), ('20120710', 3),
('20120714', 4), ('20120717', 2), ('20120728', 6)
SET DATEFIRST 7
SELECT DATEADD(DAY, 1 - DATEPART(WEEKDAY, Date), Date) AS WeekStart,
SUM(Quantity) AS Quantity
FROM #OrderHistory
GROUP BY DATEADD(DAY, 1 - DATEPART(WEEKDAY, Date), Date)
SELECT WeekStart,
SUM(Quantity) AS Quantity
FROM #OrderHistory
CROSS APPLY
( SELECT CONVERT(VARCHAR(6), DATEADD(DAY, 1 - DATEPART(WEEKDAY, Date), Date), 6) + ' to ' +
CONVERT(VARCHAR(6), DATEADD(DAY, 7 - DATEPART(WEEKDAY, Date), Date), 6) AS WeekStart
) ws
GROUP BY WeekStart
Something similar can be done for your age grouping using:
SELECT CAST(FLOOR(Age / 10.0) * 10 AS INT)
However this fails for 30-39 because there is no data for this group.
My stance on the matter would be, if you are doing the query as a one off, using a temp table, cte or case statement should work just fine, this should also extend to reusing the same query on small sets of data.
If you are likely to reuse the group however, or you are referring to significant amounts of data then create a permanent table with the ranges defined and indices applied to any columns required. This is the basis of creating dimensions in OLAP.
Couldn't you treat the age (or date) as a foreign key in a new, tiny table that is just ages (or dates) and their corresponding ranges? A join statement could provide a new table with a column that contains AgeGroups. With the new table you could use the standard group-by method.
It does seem reckless to make a new table for grouping, but it would be easy to make programatically and I think it would be easier to maintain (or drop and recreate) than a case statement or a where clause. If the result of this query is a one-off, a throwaway sql statement would probably work best, but I think my method makes the most sense for long-term use.
Well, some years ago with Oracle DB we did it the following way:
We had two tables: Sessions and Ranges. Ranges had foreign key that referenced Session.
When we needed to perform SQL, we created a new record in Sessions and several new records in Ranges that referred to that session.
Our SQL joined Ranges with filter by Session:
select sum(t.Value), r.Name
from DataTable t
join Ranges r on (r.Session = ? and r.Start t.MyDate)
group by r.Name
After we got results we deleted that record from Sessions and records from Ranges where deleted by cascade.
We had daemon job that purged Sessions from junk records that were leaked in case of extraordinary situation (killed processes, etc).
This worked perfectly. Since that time Oracle added new SQL clauses, and maybe they could be used instead. But on other RDBMSes this is still a valid way.
Another approach is to create a number of functions such as GET_YEAR_BY_DATE or GET_QUARTER_BY_DATE or GET_WEEK_BY_DATE (they would return start date of corresponding
period, for example, for any date return start date of year). And then group by them:
select sum(Value), GET_YEAR_BY_DATE(MyDate) from DataTable
group by GET_YEAR_BY_DATE(MyDate)

Select query with date condition

I would like to retrieve the records in certain dates after d/mm/yyyy, or after d/mm/yyyy and before d/mm/yyyy, how can I do it ?
SELECT date
FROM table
WHERE date > 1/09/2008;
and
SELECT date
FROM table
WHERE date > 1/09/2008;
AND date < 1/09/2010
It doesn't work.
Be careful, you're unwittingly asking "where the date is greater than one divided by nine, divided by two thousand and eight".
Put # signs around the date, like this #1/09/2008#
The semicolon character is used to terminate the SQL statement.
You can either use # signs around a date value or use Access's (ACE, Jet, whatever) cast to DATETIME function CDATE(). As its name suggests, DATETIME always includes a time element so your literal values should reflect this fact. The ISO date format is understood perfectly by the SQL engine.
Best not to use BETWEEN for DATETIME in Access: it's modelled using a floating point type and anyhow time is a continuum ;)
DATE and TABLE are reserved words in the SQL Standards, ODBC and Jet 4.0 (and probably beyond) so are best avoided for a data element names:
Your predicates suggest open-open representation of periods (where neither its start date or the end date is included in the period), which is arguably the least popular choice. It makes me wonder if you meant to use closed-open representation (where neither its start date is included but the period ends immediately prior to the end date):
SELECT my_date
FROM MyTable
WHERE my_date >= #2008-09-01 00:00:00#
AND my_date < #2010-09-01 00:00:00#;
Alternatively:
SELECT my_date
FROM MyTable
WHERE my_date >= CDate('2008-09-01 00:00:00')
AND my_date < CDate('2010-09-01 00:00:00');
select Qty, vajan, Rate,Amt,nhamali,ncommission,ntolai from SalesDtl,SalesMSt where SalesDtl.PurEntryNo=1 and SalesMST.SaleDate= (22/03/2014) and SalesMST.SaleNo= SalesDtl.SaleNo;
That should work.
hey guys i think what you are looking for is this one using select command.
With this you can specify a RANGE GREATER THAN(>) OR LESSER THAN(<) IN MySQL WITH THIS:::::
select* from <**TABLE NAME**> where year(**COLUMN NAME**) > **DATE** OR YEAR(COLUMN NAME )< **DATE**;
FOR EXAMPLE:
select name, BIRTH from pet1 where year(birth)> 1996 OR YEAR(BIRTH)< 1989;
+----------+------------+
| name | BIRTH |
+----------+------------+
| bowser | 1979-09-11 |
| chirpy | 1998-09-11 |
| whistler | 1999-09-09 |
+----------+------------+
FOR SIMPLE RANGE LIKE USE ONLY GREATER THAN / LESSER THAN
mysql>
select COLUMN NAME from <TABLE NAME> where year(COLUMN NAME)> 1996;
FOR EXAMPLE
mysql>
select name from pet1 where year(birth)> 1996 OR YEAR(BIRTH)< 1989;
+----------+
| name |
+----------+
| bowser |
| chirpy |
| whistler |
+----------+
3 rows in set (0.00 sec)

MySQL function to get list of Mondays

What is the most efficient way to get a list of all of the unique Mondays from a date field?
When I'm not all that concerned about efficiency, I have done something like:
DATE-weekday(DATE) + 1. But now I need to compute this on a large dataset and I don't want my user wishing for a Rubik's cube because it is taking so long. :)
Yes, the field is indexed.
EDIT:
What I need is a list of all of the weeks that contain records. I am creating a payroll report where the user will select the week to filter the report.
Here is what I came up with:
SELECT DISTINCT ((DATE(`timStart`)-DAYOFWEEK(`timStart`))+2)
FROM `time`
ORDER BY 1 DESC
Anyone have any improvement to suggest?
"unique mondays from a date field" should be as simple as:
SELECT DISTINCT(`date`) FROM `table` WHERE WEEKDAY(`date`)=0
"weeks in which we have date values" should be as simple as:
SELECT DISTINCT(WEEK(`date`)) FROM `table` WHERE YEAR(`date`)=2010;
SELECT WEEK(now()),YEAR(now());
+-------------+-------------+
| WEEK(now()) | YEAR(now()) |
+-------------+-------------+
| 31 | 2010 |
+-------------+-------------+
which will benefit you as well in your other payroll queries, using
WHERE WEEK(`date`)=31
Put your trust in mysql to handle things from there.