As we can have a command like this in C# or Java
return 1 < 2;
Is it possible to do something like that in SQL?
SELECT 1 < 2
or in my case:
Select
Case when sign = '<' then col1 < 10 else col1 > 10 end
from ...
Result: 0
When I use SELECT 1 < 2 I get an error:
Msg 102, Level 15, State 1, Line 1 Incorrect syntax near '<'.
So it seems that there is a problem with this statement. I'm using TSQL.
I don't know what DBMS you are using, I am using T-SQL in this example. This should work:
declare #col1 int
declare #sign varchar(1)
set #col1 = 10
set #sign = '<'
Select
case when (#sign = '<') and (#col1 < 10) then 1
when (#sign = '>') and (#col1 > 10) then 1
else 0
end
I think you mean this:
DECLARE #Col1 INT = 10; --Set your value
SELECT Result = CASE WHEN #Col1 > 10 THEN CAST(#Col1 AS VARCHAR(10) ) + ' > 10'
WHEN #Col1 < 10 THEN CAST(#Col1 AS VARCHAR(10) ) + ' < 10'
ELSE CAST(#Col1 AS VARCHAR(10) ) + ' = 10'
END
OR
DECLARE #Col1 INT = 10;
DECLARE #VarForCompare INT = 55;
SELECT Result = CASE WHEN #Col1 > #VarForCompare THEN CAST(#Col1 AS VARCHAR(10) ) + ' > ' + CAST(#VarForCompare AS VARCHAR(10) )
WHEN #Col1 < #VarForCompare THEN CAST(#Col1 AS VARCHAR(10) ) + ' < ' + + CAST(#VarForCompare AS VARCHAR(10) )
ELSE CAST(#Col1 AS VARCHAR(10) ) + ' = ' + + CAST(#VarForCompare AS VARCHAR(10) )
END
OR using IF:
IF #Col1 > #VarForCompare
SELECT CAST(#Col1 AS VARCHAR(10) ) + ' > ' + CAST(#VarForCompare AS VARCHAR(10) ) Result
ELSE
IF #Col1 < #VarForCompare
SELECT CAST(#Col1 AS VARCHAR(10) ) + ' < ' + CAST(#VarForCompare AS VARCHAR(10) ) Result
ELSE
SELECT CAST(#Col1 AS VARCHAR(10) ) + ' = ' + CAST(#VarForCompare AS VARCHAR(10) ) Result
I don't see why you need Sign variable.
select case when 1 <2 then 'true' else 'false' end
You could also use the inline if statement instead of CASE:
SELECT IIF(1 < 2,'true','false')
Related
I have a sql request below.
in the table [ServicesConf] there are 2 rows: ID = 1, 2, but after executing sql query to table [ServicesLog] 3 rows are inserted.
How to make only 2 rows be inserted?
DECLARE #cnt int = 1
,#max int = (SELECT COUNT(*) FROM [ServicesConf]);
WHILE #cnt <= #max
BEGIN
DECLARE #DateTimeNow datetime = GETDATE()
DECLARE #DateTimeNowTimeHM varchar(5) = FORMAT(#DateTimeNow, 'HH:mm') --CONVERT(varchar(5), GETDATE(),108)
DECLARE #DateTimeNowTimeM varchar(2) = FORMAT(#DateTimeNow, 'mm')
DECLARE #DateTimeNow_WeekDay varchar = (SELECT DATEPART(WEEKDAY,#DateTimeNow))
DECLARE #DateTimeNow_WeekDay_Check int = (SELECT 1 FROM [ServicesConf] WHERE [SCHEDULE_DAY] LIKE '%' + #DateTimeNow_WeekDay + '%' AND [ID] = #cnt)
DECLARE #DateTimeNow_WeekHour_Check int = (SELECT 1 FROM [ServicesConf] WHERE [SCHEDULE_HOUR] = #DateTimeNowTimeHM AND [ID] = #cnt)
DECLARE #DateTimeNow_Repeat_Check int = (SELECT 1 FROM [ServicesConf] WHERE [REPEAT] LIKE '%' + #DateTimeNowTimeM + '%' AND [ID] = #cnt)
IF (#DateTimeNow_WeekDay_Check = 1
AND #DateTimeNow_Repeat_Check = 1)
BEGIN
INSERT INTO [ServicesLog] ()
SELECT *
FROM [ServicesConf]
WHERE [REPEAT] IS NOT NULL
END
IF (#DateTimeNow_WeekDay_Check = 1
AND #DateTimeNow_WeekHour_Check = 1)
BEGIN
INSERT INTO [ServicesLog] ()
SELECT *
FROM [ServicesConf]
WHERE [REPEAT] IS NULL
END
SET #cnt = #cnt + 1
END
I have this SQL Data:
AT_SendMail_v1
AT_Certificate_v10
AT_Certificate_v100
And I want this output:
AT_SendMail_v2
AT_Certificate_v11
AT_Certificate_v101
I have this code but it doesn't work for the numbers that they more than 19:
SELECT CASE
WHEN ISNUMERIC(RIGHT([internalname],1)) = 1
THEN LEFT([internalname],LEN([internalname])-1) + cast((CONVERT(INT, RIGHT([internalname],1)) + 1) as varchar(100))
ELSE [internalname] + '_v1'
END
,[id]
FROM [task]
thanks :)
Assuming that the number is always the end of the string:
using patindex():
select
name
, new_name = case when patindex('%[_]v[1234567890]%',name) > 0
then left(name,(patindex('%[_]v[1234567890]%',name)+1))
+ convert(varchar(10),convert(int
,right(name,len(name)-(patindex('%[_]v[1234567890]%',name)+1)))
+1)
else name+'_v1'
end
from task
rextester demo: http://rextester.com/AWPXB86451
returns:
+----------------------+----------------------+
| name | new_name |
+----------------------+----------------------+
| AT_SendMail_v1 | AT_SendMail_v2 |
| AT_Certificate_v10 | AT_Certificate_v11 |
| AT_Certificate_v_v50 | AT_Certificate_v_v51 |
+----------------------+----------------------+
You could use SUBSTRING to get number after _v, after it check If It is numeric using ISNUMERIC, after It increase that number by 1, convert It to string and concat string.
DECLARE #internalname NVARCHAR(40)
SET #internalname = 'AT_SendMail_v19'
SELECT
CASE WHEN ISNUMERIC(SUBSTRING(#internalname, CHARINDEX('_v', #internalname) + 2, LEN(#internalname))) = 1
THEN SUBSTRING(#internalname,+2, CHARINDEX('_v',#internalname)) + CAST(SUBSTRING(#internalname, CHARINDEX('_v', #internalname) + 2, LEN(#internalname)) + 1 AS NVARCHAR(60))
ELSE #internalname + '_v1'
END
Try this :
declare #test varchar(50) = 'AT_SendMail_v1';
select Output = case when ISNUMERIC( substring ( #test , charindex('_v',#test)+2 , len(#test)) )=1 then
left (#test , charindex('_v',#test)+1) + cast( cast(substring ( #test , charindex('_v',#test)+2 , len(#test)) as int)+1 as varchar )
else
#test+'_v1' end;
Demo.
Here is one way to increment the numbers, you can then build it into your string...
DECLARE #str NVARCHAR(20) = 'AT_SendMail_v1';
DECLARE #intCharIndex INT = CHARINDEX('_v',#str);
IF(#intCharIndex = 0)
BEGIN
SELECT 'No string found';
END
IF(#intCharIndex > 0)
BEGIN
DECLARE #int INT = LEN(#str) - #intCharIndex - 1;
DECLARE #intVersion INT = RIGHT(#str,#int) + 1;
SELECT LEFT(#str,#intCharIndex) + 'v' + RTRIM(CAST(#intVersion AS NVARCHAR(4)));
END
that was the best code for me, thanks :)
SELECT
CASE
WHEN CHARINDEX('_v', [internalname], 1) <> 0 THEN LEFT([internalname], CHARINDEX('_v', [internalname], 1) + 1) + CAST(CAST(RIGHT([internalname], LEN([internalname]) - CHARINDEX('_v', [internalname], 1) - 1) AS INT) + 1 AS VARCHAR(100))
ELSE [internalname] + '_v1'
END
,[id]
FROM [task]
How about this that caters for ANY number after the _v
Select Left(internalName, Len(internalName) - CharIndex('v_', Reverse(internalName)) + 1) + Cast((Cast((Right(internalName, CharIndex('v_', Reverse(internalName)) -1)) As Integer) + 1) As NVarchar(32))
From task
I need to create this table function. The function needs to return single words from passed parameters like: hello, hhuu, value
The table function should return:
hello,
hhuu,
value
But I am always getting some errors, please could you help me?
you can write as:
DECLARE #input_char VARCHAR(255)
SET #input_char = 'hello, hhuu, value'
;WITH cte AS (
SELECT
CAST('<r>' + REPLACE(#input_char, ' ', '</r><r>') + '</r>' AS XML) AS input_char
)
SELECT
rtrim( LTRIM (xTable.xColumn.value('.', 'VARCHAR(MAX)')) ) AS input_char
FROM cte
CROSS APPLY input_char.nodes('//r') AS xTable(xColumn)
Please give a look at this article:
http://www.codeproject.com/Tips/625872/Convert-a-CSV-delimited-string-to-table-column-in
and you could use ' ' (space) as delimiter.
SELECT * FROM dbo.CSVtoTable('hello, hhuu, value', ' ')
I have used the following function many times. It is a bit lengthy forgive me, but it has become a great tool for me.
CREATE Function [dbo].[ParseText2Table]
(
#p_SourceText varchar(MAX)
,#p_Delimeter varchar(100) = ',' --default to comma delimited.
)
RETURNS #retTable TABLE
(
POSITION INT
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
AS
BEGIN
DECLARE #tmpTable TABLE
(
Position2 INT IDENTITY(1,1) PRIMARY KEY
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
DECLARE #w_Continue INT
,#w_StartPos INT
,#w_Length INT
,#w_Delimeter_pos INT
,#w_tmp_int bigint
,#w_tmp_num REAL--numeric(18,3)
,#w_tmp_txt varchar(MAX)
,#w_Delimeter_Len INT
IF len(#p_SourceText) = 0
BEGIN
SET #w_Continue = 0 -- force early exit
END
ELSE
BEGIN
-- if delimiter is ' ' change
IF #p_Delimeter = ' '
BEGIN
SET #p_SourceText = replace(#p_SourceText,' ','ÿ')
SET #p_Delimeter = 'ÿ'
END
-- parse the original #p_SourceText array into a temp table
SET #w_Continue = 1
SET #w_StartPos = 1
SET #p_SourceText = RTRIM( LTRIM( #p_SourceText))
SET #w_Length = DATALENGTH( RTRIM( LTRIM( #p_SourceText)))
SET #w_Delimeter_Len = len(#p_Delimeter)
END
WHILE #w_Continue = 1
BEGIN
SET #w_Delimeter_pos = CHARINDEX( #p_Delimeter
,(SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)))
)
IF #w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,(#w_Delimeter_pos - 1)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL END
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SET #w_StartPos = #w_Delimeter_pos + #w_StartPos + (#w_Delimeter_Len- 1)
END
ELSE -- No more delimeters, get last value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--as numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL end
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SELECT #w_Continue = 0
END
INSERT INTO #tmpTable VALUES( #w_tmp_int, #w_tmp_num, #w_tmp_txt )
END
INSERT INTO #retTable SELECT Position2, Int_Value ,Num_value ,txt_value FROM #tmpTable
RETURN
END
Here are the supporting functions for above as well:
CREATE FUNCTION dbo.isReallyInteger
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', #num) = 0
AND CHARINDEX('-', #num) <= 1
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
CREATE FUNCTION dbo.isReallyNumeric
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
DECLARE #pos TINYINT
SET #pos = 1 + LEN(#num) - CHARINDEX('.', REVERSE(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', #num) = 0
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
AND
(
((#pos = LEN(#num)+1)
OR #pos = CHARINDEX('.', #num))
)
THEN
1
ELSE
0
END
END
Usage Examples:
--Single Character Delimiter
--select * from dbo.ParseText2Table('100|120|130.56|Yes|Cobalt|Blue','|')
--select txt_value from dbo.ParseText2Table('100 120 130.56 Yes Cobalt Blue',' ') where Position = 3
--select * from dbo.ParseText2Table('100,120,130.56,Yes,Cobalt,Blue,,',',')
/*
POSITION Int_Value Num_value txt_value
----------- ----------- -------------------- --------------
1 100 100.000 100
2 120 120.000 120
3 131 130.560 130.56
4 NULL NULL Yes
5 NULL NULL Cobalt Blue
*/
I want to find a credit card numeric value in a sql string.
for example;
DECLARE #value1 NVARCHAR(MAX) = 'The payment is the place 1234567812345678'
DECLARE #value2 NVARCHAR(MAX) = 'The payment is the place 123456aa7812345678'
DECLARE #value3 NVARCHAR(MAX) = 'The payment1234567812345678is the place'
The result should be :
#value1Result 1234567812345678
#value2Result NULL
#value3Result 1234567812345678
16 digits must be together without space.
How to do this in a sql script or a function?
edit :
if I want to find these 2 credit card value.
#value4 = 'card 1 is : 4034349183539301 and the other one is 3456123485697865'
how should I implement the scripts?
You can use PathIndex as
PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', yourStr)
if the result is 0 then it doesnt containg 16 digits other was it contains.
It can be used withing a Where statement or Select statement based on your needs
You can write as:
SELECT case when Len(LEFT(subsrt, PATINDEX('%[^0-9]%', subsrt + 't') - 1)) = 16
then LEFT(subsrt, PATINDEX('%[^0-9]%', subsrt + 't') - 1)
else ''
end
FROM (
SELECT subsrt = SUBSTRING(string, pos, LEN(string))
FROM (
SELECT string, pos = PATINDEX('%[0-9]%', string)
FROM table1
) d
) t
Demo
DECLARE #value1 NVARCHAR(MAX) = 'card 1 is : 4034349183539301 and the other one is 3456123485697865'
DECLARE #Lenght INT
,#Count INT
,#Candidate CHAR
,#cNum INT
,#result VARCHAR(16)
SELECT #Count = 1
SELECT #cNum = 0
SELECT #result = ''
SELECT #Lenght = LEN(#value1)
WHILE #Count <= #Lenght
BEGIN
SELECT #Candidate = SUBSTRING(#value1, #Count, 1)
IF #Candidate != ' '
AND ISNUMERIC(#Candidate) = 1
BEGIN
SET #cNum = #cNum + 1
SET #result = #result + #Candidate
END
ELSE
BEGIN
SET #cNum = 1
SET #result = ''
END
IF #cNum > 16
BEGIN
SELECT #result 'Credit Number'
END
SET #Count = #Count + 1
END
There you go kind sir.
DECLARE
#value3 NVARCHAR(MAX) = 'The payment1234567812345678is the place',
#MaxCount int,
#Count int,
#Numbers NVARCHAR(100)
SELECT #Count = 1
SELECT #Numbers = ''
SELECT #MaxCount = LEN(#value3)
WHILE #Count <= #MaxCount
BEGIN
IF (UNICODE(SUBSTRING(#value3,#Count,1)) >= 48 AND UNICODE(SUBSTRING(#value3,#Count,1)) <=57)
SELECT #Numbers = #Numbers + SUBSTRING(#value3,#Count,1)
SELECT #Count = #Count + 1
END
PRINT #Numbers
You can make this as a function if you are planning to use it a lot.
I'm programming a function in SQL 2008R2 that i could give it some parameters like a value varchar, a pattern varchar, a separator char and a filler also char. Then I would like to give the value '22687' with the patter '000.000.000.000', a separator '.' and the filler would be '0', then i would like to expect the function will return '000.000.022.687', does any one have a function already done that can do this?
Something like this:
DECLARE #valor VARCHAR(30)
DECLARE #formato VARCHAR(30)
DECLARE #separador CHAR(1)
DECLARE #rellenarcon CHAR(1)
SELECT #valor = '22959'
SELECT #formato = '000.000.000.000'
SELECT #separador = '.'
SELECT #rellenarcon = '0'
DECLARE #n INTEGER
DECLARE #m INTEGER
DECLARE #i INTEGER
DECLARE #j INTEGER
SELECT #n = LEN(#formato)
SELECT #m = LEN(#valor)
SELECT #i = 1
SELECT #j = 1
DECLARE #res2 varchar(30)
SELECT #res2 = ''
SELECT #valor = REVERSE(#valor)
WHILE #i<=#n
BEGIN
if SUBSTRING(#formato,#i,1) <> #separador
begin
IF #j<=#m
BEGIN
SELECT #res2 = #res2 + SUBSTRING(#valor,#j,1)
SELECT #i=#i+1
SELECT #j=#j+1
END
ELSE
BEGIN
SELECT #res2 = #res2 + #rellenarcon
SELECT #i=#i+1
END
end
else
BEGIN
SELECT #res2 = #res2 + #separador
SELECT #i=#i+1
END
END
print reverse(#res2)
Is a crossover code from java to tsql, the original code in java is:
public static String formatear(String valor, String formato, char separator,
char fillWith, Map<Integer, String> params) {
int n = formato.length() - 1;
int m = valor.length() - 1;
int i = n;
int j = m;
StringBuilder res = new StringBuilder(formato);
for(; i >= 0; i--) {
if(res.charAt(i) != separator) {
if(j >= 0) {
res.deleteCharAt(i);
res.insert(i, valor.charAt(j--));
} else {
res.deleteCharAt(i);
res.insert(i, fillWith);
}
}
}
if(params != null) {
Set<Integer> keys = params.keySet();
for(Integer key : keys) {
i = key;
res.deleteCharAt(i);
res.insert(i, params.get(key));
}
}
return res.toString();
}
The following assumes well-formed inputs, e.g. the value is not longer than the pattern.
declare #Pattern as VarChar(64) = '000.000.000.000';
declare #Fill as Char = '0';
declare #Value as VarChar(64) = '22687';
declare #False as Bit = 0;
declare #True as Bit = 1;
with Gargoyle as (
select #Pattern as Pattern, #Value as Value, Cast( '' as VarChar(64) ) as Buffer,
case when Right( #Pattern, 1 ) = #Fill then #True else #False end as Fill
union all
select
-- Always consume a character from the pattern.
Left( Pattern, Len( Pattern ) - 1 ),
-- Consume a character from the value if the pattern contains fill at the current position.
case
when Fill = #True and Value != '' then Left( Value, Len( Value ) - 1 )
else Value end,
-- Add the correct character to the buffer.
Cast( case when Fill = #True and Value != '' then Right( Value, 1 ) else Right( Pattern, 1 ) end + Buffer as VarChar(64) ),
-- Check the next pattern character for fill.
case
when Len( Pattern ) = 1 then #False
when Substring( Pattern, Len( Pattern ) - 1, 1 ) = #Fill then #True
else #False end
from Gargoyle
where Pattern != ''
)
select Buffer
from Gargoyle
where Pattern = '';
Or, as a function:
create function dbo.PatternFill( #Pattern as VarChar(64), #Fill as Char, #Value as VarChar(64) )
returns VarChar(64)
as
begin
declare #Buffer as VarChar(64) = ''
declare #PatternChar as Char = Right( #Pattern, 1 )
declare #ValueChar as Char = Right( #Value, 1 )
while #Pattern != ''
begin
if #PatternChar = #Fill and #ValueChar != ''
begin
-- Replace a fill character with a value character.
select #Buffer = #ValueChar + #Buffer
if Len( #Value ) > 1
select #Value = Left( #Value, Len( #Value ) - 1 ), #ValueChar = Right( #Value, 1 )
else
select #ValueChar = '', #Value = ''
end
else
begin
-- Copy the pattern character.
select #Buffer = #PatternChar + #Buffer
end
if Len( #Pattern ) > 1
select #Pattern = Left( #Pattern, Len( #Pattern ) - 1 ), #PatternChar = Right( #Pattern, 1 )
else
select #PatternChar = '', #Pattern = ''
end
return #Buffer
end
go
declare #Result as VarChar(64)
declare #Count as Int = 1000000
declare #Start as DateTime = GetDate()
while #Count > 0
select #Result = dbo.PatternFill( '000.000.000.000', '0', '22687' ), #Count = #Count - 1
select #Result as [Result], DateDiff( ms, #Start, GetDate() ) as [Total ms]
1,000,000 iterations on my notebook took 151,656ms, but it's busy BOINCing. That's simple timing with no correction for the time consumed by an empty loop or calling an empty function.
This query will do it:
;with cteZeroPadded(Num) as
(
Select Right('000000000000' + '22687', 12)
)
,cteSplit as
(
Select SUBSTRING(Num, 1, 3) Col1
,SUBSTRING(Num, 4, 3) Col2
,SUBSTRING(Num, 7, 3) Col3
,SUBSTRING(Num, 10, 3) Col4
From cteZeroPadded
)
Select Col1 + '.' + Col2 + '.' + Col3 + '.' + Col4
From cteSplit
In SQLServer2005+ you can use option with recursive CTE
DECLARE #valor varchar(30) = '22959',
#formato varchar(30) = '000000000000000',
#text varchar(30),
#result varchar(30) = N''
SET #text = REVERSE(RIGHT(#formato + #valor, 15))
;WITH cte AS
(
SELECT 1 AS Number, SUBSTRING(#text, 1, 1) AS Num
UNION ALL
SELECT c.Number + 1,
CASE WHEN c.Number IN(3, 7, 11) THEN '.' ELSE
SUBSTRING(#text, CASE WHEN c.Number > 11 THEN c.Number - 2
WHEN c.Number > 7 THEN c.Number - 1
WHEN c.Number > 3 THEN c.Number
ELSE c.Number + 1
END, 1)
END
FROM cte c
WHERE Number < LEN(#text)
)
SELECT #result += c.Num
FROM cte c
ORDER BY Number DESC
SELECT #result
See demo on SQLFiddle