mysql function to pretty print sizes (pg_size_pretty equivialent)? - mysql

postgresql have the pg_size_pretty() convenience function:
> select pg_size_pretty(100000);
pg_size_pretty
----------------
98 kB
Does MySQL have something similar ?
If not, before I make my own have anyone already made this, that they can share ?

There is no shipped function like that in MySQL.
So, I just created one : function pretty_size
https://github.com/lqez/pastebin/blob/master/mysql/pretty_size.sql
CREATE FUNCTION pretty_size( size DOUBLE )
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
DECLARE c INT DEFAULT 0;
DECLARE unit INT DEFAULT 1000;
DECLARE unitChar CHAR(6) DEFAULT 'KMGTPE';
DECLARE binaryPrefix BOOLEAN DEFAULT 1;
/* Set binaryPrefix = 1 to use binary unit & prefix */
/* See IEC 60027-2 A.2 and ISO/IEC 80000 */
IF binaryPrefix = 1 THEN
SET unit = 1024;
END IF;
WHILE size >= unit AND c < 6 DO
SET size = size / unit;
SET c = c + 1;
END WHILE;
/* Under 1K, just add 'Byte(s)' */
IF c = 0 THEN
RETURN CONCAT( size, ' B' );
END IF;
/* Modify as your taste */
RETURN CONCAT(
FORMAT( size, 2 ),
' ',
SUBSTR( unitChar, c, 1 ),
IF( binaryPrefix, 'iB', 'B' )
);
END $$
DELIMITER ;

pg_size_pretty
will give you Kb or MB or GB ,etc according to the internal code ,and you wont operate or sum this result ...
better use :
pg_relation_size :
returns the on-disk size in bytes ,so you can convert it to the format (kb,mb,gb) that you want.

Related

How to decode BASE64 text (from JSON data) in TSQL with accents intact [duplicate]

I have a column in SQL Server with utf8 SQL_Latin1_General_CP1_CI_AS encoding. How can I convert and save the text in ISO 8859-1 encoding? I would like to do thing in a query on SQL Server. Any tips?
Olá. Gostei do jogo. Quando "baixei" até achei que não iria curtir muito
I have written a function to repair UTF-8 text that is stored in a varchar field.
To check the fixed values you can use it like this:
CREATE TABLE #Table1 (Column1 varchar(max))
INSERT #Table1
VALUES ('Olá. Gostei do jogo. Quando "baixei" até achei que não iria curtir muito')
SELECT *, NewColumn1 = dbo.DecodeUTF8String(Column1)
FROM Table1
WHERE Column1 <> dbo.DecodeUTF8String(Column1)
Output:
Column1
-------------------------------
Olá. Gostei do jogo. Quando "baixei" até achei que não iria curtir muito
NewColumn1
-------------------------------
Olá. Gostei do jogo. Quando "baixei" até achei que não iria curtir muito
The code:
CREATE FUNCTION dbo.DecodeUTF8String (#value varchar(max))
RETURNS nvarchar(max)
AS
BEGIN
-- Transforms a UTF-8 encoded varchar string into Unicode
-- By Anthony Faull 2014-07-31
DECLARE #result nvarchar(max);
-- If ASCII or null there's no work to do
IF (#value IS NULL
OR #value NOT LIKE '%[^ -~]%' COLLATE Latin1_General_BIN
)
RETURN #value;
-- Generate all integers from 1 to the length of string
WITH e0(n) AS (SELECT TOP(POWER(2,POWER(2,0))) NULL FROM (VALUES (NULL),(NULL)) e(n))
, e1(n) AS (SELECT TOP(POWER(2,POWER(2,1))) NULL FROM e0 CROSS JOIN e0 e)
, e2(n) AS (SELECT TOP(POWER(2,POWER(2,2))) NULL FROM e1 CROSS JOIN e1 e)
, e3(n) AS (SELECT TOP(POWER(2,POWER(2,3))) NULL FROM e2 CROSS JOIN e2 e)
, e4(n) AS (SELECT TOP(POWER(2,POWER(2,4))) NULL FROM e3 CROSS JOIN e3 e)
, e5(n) AS (SELECT TOP(POWER(2.,POWER(2,5)-1)-1) NULL FROM e4 CROSS JOIN e4 e)
, numbers(position) AS
(
SELECT TOP(DATALENGTH(#value)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM e5
)
-- UTF-8 Algorithm (http://en.wikipedia.org/wiki/UTF-8)
-- For each octet, count the high-order one bits, and extract the data bits.
, octets AS
(
SELECT position, highorderones, partialcodepoint
FROM numbers a
-- Split UTF8 string into rows of one octet each.
CROSS APPLY (SELECT octet = ASCII(SUBSTRING(#value, position, 1))) b
-- Count the number of leading one bits
CROSS APPLY (SELECT highorderones = 8 - FLOOR(LOG( ~CONVERT(tinyint, octet) * 2 + 1)/LOG(2))) c
CROSS APPLY (SELECT databits = 7 - highorderones) d
CROSS APPLY (SELECT partialcodepoint = octet % POWER(2, databits)) e
)
-- Compute the Unicode codepoint for each sequence of 1 to 4 bytes
, codepoints AS
(
SELECT position, codepoint
FROM
(
-- Get the starting octect for each sequence (i.e. exclude the continuation bytes)
SELECT position, highorderones, partialcodepoint
FROM octets
WHERE highorderones <> 1
) lead
CROSS APPLY (SELECT sequencelength = CASE WHEN highorderones in (1,2,3,4) THEN highorderones ELSE 1 END) b
CROSS APPLY (SELECT endposition = position + sequencelength - 1) c
CROSS APPLY
(
-- Compute the codepoint of a single UTF-8 sequence
SELECT codepoint = SUM(POWER(2, shiftleft) * partialcodepoint)
FROM octets
CROSS APPLY (SELECT shiftleft = 6 * (endposition - position)) b
WHERE position BETWEEN lead.position AND endposition
) d
)
-- Concatenate the codepoints into a Unicode string
SELECT #result = CONVERT(xml,
(
SELECT NCHAR(codepoint)
FROM codepoints
ORDER BY position
FOR XML PATH('')
)).value('.', 'nvarchar(max)');
RETURN #result;
END
GO
Jason Penny has also written an SQL function to convert UTF-8 to Unicode (MIT licence) which worked on a simple example for me:
CREATE FUNCTION dbo.UTF8_TO_NVARCHAR(#in VarChar(MAX))
RETURNS NVarChar(MAX)
AS
BEGIN
DECLARE #out NVarChar(MAX), #i int, #c int, #c2 int, #c3 int, #nc int
SELECT #i = 1, #out = ''
WHILE (#i <= Len(#in))
BEGIN
SET #c = Ascii(SubString(#in, #i, 1))
IF (#c < 128)
BEGIN
SET #nc = #c
SET #i = #i + 1
END
ELSE IF (#c > 191 AND #c < 224)
BEGIN
SET #c2 = Ascii(SubString(#in, #i + 1, 1))
SET #nc = (((#c & 31) * 64 /* << 6 */) | (#c2 & 63))
SET #i = #i + 2
END
ELSE
BEGIN
SET #c2 = Ascii(SubString(#in, #i + 1, 1))
SET #c3 = Ascii(SubString(#in, #i + 2, 1))
SET #nc = (((#c & 15) * 4096 /* << 12 */) | ((#c2 & 63) * 64 /* << 6 */) | (#c3 & 63))
SET #i = #i + 3
END
SET #out = #out + NChar(#nc)
END
RETURN #out
END
GO
The ticked answer by Anthony "looks" better to me, but maybe run both if doing conversion and investigate any discrepencies?!
Also we used the very ugly code below to detect BMP page unicode characters that were encoded as UTF-8 and then converted from varchar to nvarchar fields, that can be converted to UCS-16.
LIKE (N'%[' + CONVERT(NVARCHAR,(CHAR(192))) + CONVERT(NVARCHAR,(CHAR(193))) + CONVERT(NVARCHAR,(CHAR(194))) + CONVERT(NVARCHAR,(CHAR(195))) + CONVERT(NVARCHAR,(CHAR(196))) + CONVERT(NVARCHAR,(CHAR(197))) + CONVERT(NVARCHAR,(CHAR(198))) + CONVERT(NVARCHAR,(CHAR(199))) + CONVERT(NVARCHAR,(CHAR(200))) + CONVERT(NVARCHAR,(CHAR(201))) + CONVERT(NVARCHAR,(CHAR(202))) + CONVERT(NVARCHAR,(CHAR(203))) + CONVERT(NVARCHAR,(CHAR(204))) + CONVERT(NVARCHAR,(CHAR(205))) + CONVERT(NVARCHAR,(CHAR(206))) + CONVERT(NVARCHAR,(CHAR(207))) + CONVERT(NVARCHAR,(CHAR(208))) + CONVERT(NVARCHAR,(CHAR(209))) + CONVERT(NVARCHAR,(CHAR(210))) + CONVERT(NVARCHAR,(CHAR(211))) + CONVERT(NVARCHAR,(CHAR(212))) + CONVERT(NVARCHAR,(CHAR(213))) + CONVERT(NVARCHAR,(CHAR(214))) + CONVERT(NVARCHAR,(CHAR(215))) + CONVERT(NVARCHAR,(CHAR(216))) + CONVERT(NVARCHAR,(CHAR(217))) + CONVERT(NVARCHAR,(CHAR(218))) + CONVERT(NVARCHAR,(CHAR(219))) + CONVERT(NVARCHAR,(CHAR(220))) + CONVERT(NVARCHAR,(CHAR(221))) + CONVERT(NVARCHAR,(CHAR(222))) + CONVERT(NVARCHAR,(CHAR(223))) + CONVERT(NVARCHAR,(CHAR(224))) + CONVERT(NVARCHAR,(CHAR(225))) + CONVERT(NVARCHAR,(CHAR(226))) + CONVERT(NVARCHAR,(CHAR(227))) + CONVERT(NVARCHAR,(CHAR(228))) + CONVERT(NVARCHAR,(CHAR(229))) + CONVERT(NVARCHAR,(CHAR(230))) + CONVERT(NVARCHAR,(CHAR(231))) + CONVERT(NVARCHAR,(CHAR(232))) + CONVERT(NVARCHAR,(CHAR(233))) + CONVERT(NVARCHAR,(CHAR(234))) + CONVERT(NVARCHAR,(CHAR(235))) + CONVERT(NVARCHAR,(CHAR(236))) + CONVERT(NVARCHAR,(CHAR(237))) + CONVERT(NVARCHAR,(CHAR(238))) + CONVERT(NVARCHAR,(CHAR(239)))
+ N'][' + CONVERT(NVARCHAR,(CHAR(128))) + CONVERT(NVARCHAR,(CHAR(129))) + CONVERT(NVARCHAR,(CHAR(130))) + CONVERT(NVARCHAR,(CHAR(131))) + CONVERT(NVARCHAR,(CHAR(132))) + CONVERT(NVARCHAR,(CHAR(133))) + CONVERT(NVARCHAR,(CHAR(134))) + CONVERT(NVARCHAR,(CHAR(135))) + CONVERT(NVARCHAR,(CHAR(136))) + CONVERT(NVARCHAR,(CHAR(137))) + CONVERT(NVARCHAR,(CHAR(138))) + CONVERT(NVARCHAR,(CHAR(139))) + CONVERT(NVARCHAR,(CHAR(140))) + CONVERT(NVARCHAR,(CHAR(141))) + CONVERT(NVARCHAR,(CHAR(142))) + CONVERT(NVARCHAR,(CHAR(143))) + CONVERT(NVARCHAR,(CHAR(144))) + CONVERT(NVARCHAR,(CHAR(145))) + CONVERT(NVARCHAR,(CHAR(146))) + CONVERT(NVARCHAR,(CHAR(147))) + CONVERT(NVARCHAR,(CHAR(148))) + CONVERT(NVARCHAR,(CHAR(149))) + CONVERT(NVARCHAR,(CHAR(150))) + CONVERT(NVARCHAR,(CHAR(151))) + CONVERT(NVARCHAR,(CHAR(152))) + CONVERT(NVARCHAR,(CHAR(153))) + CONVERT(NVARCHAR,(CHAR(154))) + CONVERT(NVARCHAR,(CHAR(155))) + CONVERT(NVARCHAR,(CHAR(156))) + CONVERT(NVARCHAR,(CHAR(157))) + CONVERT(NVARCHAR,(CHAR(158))) + CONVERT(NVARCHAR,(CHAR(159))) + CONVERT(NVARCHAR,(CHAR(160))) + CONVERT(NVARCHAR,(CHAR(161))) + CONVERT(NVARCHAR,(CHAR(162))) + CONVERT(NVARCHAR,(CHAR(163))) + CONVERT(NVARCHAR,(CHAR(164))) + CONVERT(NVARCHAR,(CHAR(165))) + CONVERT(NVARCHAR,(CHAR(166))) + CONVERT(NVARCHAR,(CHAR(167))) + CONVERT(NVARCHAR,(CHAR(168))) + CONVERT(NVARCHAR,(CHAR(169))) + CONVERT(NVARCHAR,(CHAR(170))) + CONVERT(NVARCHAR,(CHAR(171))) + CONVERT(NVARCHAR,(CHAR(172))) + CONVERT(NVARCHAR,(CHAR(173))) + CONVERT(NVARCHAR,(CHAR(174))) + CONVERT(NVARCHAR,(CHAR(175))) + CONVERT(NVARCHAR,(CHAR(176))) + CONVERT(NVARCHAR,(CHAR(177))) + CONVERT(NVARCHAR,(CHAR(178))) + CONVERT(NVARCHAR,(CHAR(179))) + CONVERT(NVARCHAR,(CHAR(180))) + CONVERT(NVARCHAR,(CHAR(181))) + CONVERT(NVARCHAR,(CHAR(182))) + CONVERT(NVARCHAR,(CHAR(183))) + CONVERT(NVARCHAR,(CHAR(184))) + CONVERT(NVARCHAR,(CHAR(185))) + CONVERT(NVARCHAR,(CHAR(186))) + CONVERT(NVARCHAR,(CHAR(187))) + CONVERT(NVARCHAR,(CHAR(188))) + CONVERT(NVARCHAR,(CHAR(189))) + CONVERT(NVARCHAR,(CHAR(190))) + CONVERT(NVARCHAR,(CHAR(191)))
+ N']%') COLLATE Latin1_General_BIN
The above:
detects multi-byte sequences encoding U+0080 to U+FFFF (U+0080 to U+07FF is encoded as 110xxxxx 10xxxxxx, U+0800 to U+FFFF is encoded as 1110xxxx 10xxxxxx 10xxxxxx)
i.e. it detects hex byte 0xC0 to 0xEF followed by hex byte 0x80 to 0xBF
ignores ASCII control characters U+0000 to U+001F
ignores characters that are already correctly encoded to unicode >= U+0100 (i.e. not UTF-8)
ignores unicode characters U+0080 to U+00FF if they don't appear to be part of a UTF-8 sequence e.g. "coöperatief".
doesn't use LIKE "%[X-Y]" for X=0x80 to Y=0xBF because of potential collation issues
uses CONVERT(VARCHAR,CHAR(X)) instead of NCHAR because we had problems with NCHAR getting converted to the wrong value (for some values).
ignores UTF characters greater than U+FFFF (4 to 6 byte sequences which have a first byte of hex 0xF0 to 0xFD)
I made a solution that also handles 4 byte sequences (like emojis) by combining the answer from #robocat, some more cases with the logic taken from https://github.com/benkasminbullock/unicode-c/blob/master/unicode.c, and a solution for the problem of encoding extended unicode characters from https://dba.stackexchange.com/questions/139551/how-do-i-set-a-sql-server-unicode-nvarchar-string-to-an-emoji-or-supplementary. It's not fast or pretty, but it's working for me anyway. This particular solution includes Unicode replacement characters wherever it finds unknown bytes. It may be better just to throw an exception in these cases, or leave the bytes as they were, as future encoding could be off, but I preferred this for my use case.
-- Started with https://stackoverflow.com/questions/28168055/convert-text-value-in-sql-server-from-utf8-to-iso-8859-1
-- Modified following source in https://github.com/benkasminbullock/unicode-c/blob/master/unicode.c
-- Made characters > 65535 work using https://dba.stackexchange.com/questions/139551/how-do-i-set-a-sql-server-unicode-nvarchar-string-to-an-emoji-or-supplementary
CREATE FUNCTION dbo.UTF8_TO_NVARCHAR(#in VarChar(MAX)) RETURNS NVarChar(MAX) AS
BEGIN
DECLARE #out NVarChar(MAX), #thisOut NVARCHAR(MAX), #i int, #c int, #c2 int, #c3 int, #c4 int
SELECT #i = 1, #out = ''
WHILE (#i <= Len(#in)) BEGIN
SET #c = Ascii(SubString(#in, #i, 1))
IF #c <= 0x7F BEGIN
SET #thisOut = NCHAR(#c)
SET #i = #i + 1
END
ELSE IF #c BETWEEN 0xC2 AND 0xDF BEGIN
SET #c2 = Ascii(SubString(#in, #i + 1, 1))
IF #c2 < 0x80 OR #c2 > 0xBF BEGIN
SET #thisOut = NCHAR(0xFFFD)
SET #i = #i + 1
END
ELSE BEGIN
SET #thisOut = NCHAR(((#c & 31) * 64 /* << 6 */) | (#c2 & 63))
SET #i = #i + 2
END
END
ELSE IF #c BETWEEN 0xE0 AND 0xEF BEGIN
SET #c2 = Ascii(SubString(#in, #i + 1, 1))
SET #c3 = Ascii(SubString(#in, #i + 2, 1))
IF #c2 < 0x80 OR #c2 > 0xBF OR #c3 < 0x80 OR (#c = 0xE0 AND #c2 < 0xA0) BEGIN
SET #thisOut = NCHAR(0xFFFD)
SET #i = #i + 1
END
ELSE BEGIN
SET #thisOut = NCHAR(((#c & 15) * 4096 /* << 12 */) | ((#c2 & 63) * 64 /* << 6 */) | (#c3 & 63))
SET #i = #i + 3
END
END
ELSE IF #c BETWEEN 0xF0 AND 0xF4 BEGIN
SET #c2 = Ascii(SubString(#in, #i + 1, 1))
SET #c3 = Ascii(SubString(#in, #i + 2, 1))
SET #c4 = Ascii(SubString(#in, #i + 3, 1))
IF #c2 < 0x80 OR #c2 >= 0xC0 OR #c3 < 0x80 OR #c3 >= 0xC0 OR #c4 < 0x80 OR #c4 >= 0xC0 OR (#c = 0xF0 AND #c2 < 0x90) BEGIN
SET #thisOut = NCHAR(0xFFFD)
SET #i = #i + 1
END
ELSE BEGIN
DECLARE #nc INT = (((#c & 0x07) * 262144 /* << 18 */) | ((#c2 & 0x3F) * 4096 /* << 12 */) | ((#c3 & 0x3F) * 64) | (#c4 & 0x3F))
DECLARE #HighSurrogateInt INT = 55232 + (#nc / 1024), #LowSurrogateInt INT = 56320 + (#nc % 1024)
SET #thisOut = NCHAR(#HighSurrogateInt) + NCHAR(#LowSurrogateInt)
SET #i = #i + 4
END
END
ELSE BEGIN
SET #thisOut = NCHAR(0xFFFD)
SET #i = #i + 1
END
SET #out = #out + #thisOut
END
RETURN #out
END
GO
i add a little modification to use new string aggregation function string_agg, from sql server 2017 and 2019
SELECT #result=STRING_AGG(NCHAR([codepoint]),'') WITHIN GROUP (ORDER BY position ASC)
FROM codepoints
change de #result parts to this one. The XML still work in old fashion way.
in 2019, string_agg works extreme faster than xml version (obvious... string_agg now is native, and is not fair compare)
Here's my version written as an inline table-valued function (TVF) for SQL Server 2017. It is limited to 4000 byte input strings as that was more than enough for my needs. Limiting the input size and writing as a TVF makes this version significantly faster than the scaler valued functions posted so far. It also handles four-byte UTF-8 sequences (such as those created by emoji), which cannot be represented in UCS-2 strings, by outputting a replacement character in their place.
CREATE OR ALTER FUNCTION [dbo].[fnUTF8Decode](#UTF8 VARCHAR(4001)) RETURNS TABLE AS RETURN
/* Converts a UTF-8 encoded VARCHAR to NVARCHAR (UCS-2). Based on UTF-8 documentation on Wikipedia and the
code/discussion at https://stackoverflow.com/a/31064459/1979220.
One can quickly detect strings that need conversion using the following expression:
<FIELD> LIKE CONCAT('%[', CHAR(192), '-', CHAR(255), ']%') COLLATE Latin1_General_BIN.
Be aware, however, that this may return true for strings that this function has already converted to UCS-2.
See robocat's answer on the above referenced Stack Overflow thread for a slower but more robust expression.
Notes/Limitations
1) Written as a inline table-valued function for optimized performance.
2) Only tested on a database with SQL_Latin1_General_CP1_CI_AS collation. More specifically, this was
not designed to output Supplementary Characters and converts all such UTF-8 sequences to �.
3) Empty input strings, '', and strings with nothing but invalid UTF-8 chars are returned as NULL.
4) Assumes input is UTF-8 compliant. For example, extended ASCII characters such as en dash CHAR(150)
are not allowed unless part of a multi-byte sequence and will be skipped otherwise. In other words:
SELECT * FROM dbo.fnUTF8Decode(CHAR(150)) -> NULL
5) Input should be limited to 4000 characters to ensure that output will fit in NVARCHAR(4000), which is
what STRING_AGG outputs when fed a sequence of NVARCHAR(1) characters generated by NCHAR. However,
T-SQL silently truncates overlong parameters so we've declared our input as VARCHAR(4001) to allow
STRING_AGG to generate an error on overlong input. If we didn't do this, callers would never be
notified about truncation.
6) If we need to process more than 4000 chars in the future, we'll need to change input to VARCHAR(MAX) and
CAST the CASE WHEN expression to NVARCHAR(MAX) to force STRING_AGG to output NVARCHAR(MAX). Note that
this change will significantly degrade performance, which is why we didn't do it in the first place.
7) Due to use of STRING_AGG, this is only compatible with SQL 2017. It will probably work fine on 2019
but that version has native UTF-8 support so you're probably better off using that. For earlier versions,
replace STRING_AGG with a CLR equivalent (ms-sql-server-group-concat-sqlclr) or FOR XML PATH(''), TYPE...
*/
SELECT STRING_AGG (
CASE
WHEN A1 & 0xF0 = 0xF0 THEN --Four byte sequences (like emoji) can't be represented in UCS-2
NCHAR(0xFFFD) --Output U+FFFD (Replacement Character) instead
WHEN A1 & 0xE0 = 0xE0 THEN --Three byte sequence; get/combine relevant bits from A1-A3
NCHAR((A1 & 0x0F) * 4096 | (A2 & 0x3F) * 64 | (A3 & 0x3F))
WHEN A1 & 0xC0 = 0xC0 THEN --Two byte sequence; get/combine relevant bits from A1-A2
NCHAR((A1 & 0x3F) * 64 | (A2 & 0x3F))
ELSE NCHAR(A1) --Regular ASCII character; output as is
END
, '') UCS2
FROM dbo.fnNumbers(ISNULL(DATALENGTH(#UTF8), 0))
CROSS APPLY (SELECT ASCII(SUBSTRING(#UTF8, I, 1)) A1, ASCII(SUBSTRING(#UTF8, I + 1, 1)) A2, ASCII(SUBSTRING(#UTF8, I + 2, 1)) A3) A
WHERE A1 <= 127 OR A1 >= 192 --Output only ASCII chars and one char for each multi-byte sequence
GO
Note that the above requires a "Numbers" table or generator function. Here's the function I use:
CREATE OR ALTER FUNCTION [dbo].[fnNumbers](#MaxNumber BIGINT) RETURNS TABLE AS RETURN
/* Generates a table of numbers up to the specified #MaxNumber, limited to 4,294,967,296. Useful for special case
situations and algorithms. Copied from https://www.itprotoday.com/sql-server/virtual-auxiliary-table-numbers
with minor formatting and name changes.
*/
WITH L0 AS (
SELECT 1 I UNION ALL SELECT 1 --Generates 2 rows
), L1 AS (
SELECT 1 I FROM L0 CROSS JOIN L0 L -- 4 rows
), L2 AS (
SELECT 1 I FROM L1 CROSS JOIN L1 L -- 16 rows
), L3 AS (
SELECT 1 I FROM L2 CROSS JOIN L2 L -- 256 rows
), L4 AS (
SELECT 1 I FROM L3 CROSS JOIN L3 L -- 65,536 rows
), L5 AS (
SELECT 1 I FROM L4 CROSS JOIN L4 L -- 4,294,967,296 rows
), Numbers AS (
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) I FROM L5
)
SELECT TOP (#MaxNumber) I FROM Numbers ORDER BY I
GO
I just succeeded by creating a new field as varchar(255) and setting the new field to the old field which was nvarchar(255). This produced the 'Americanized' version of the international places.
Update WorldCities
Set admin_correct = admin_name
varchar(255) nvarchar(255)
I found the query I need to do, just not the encoding yet.
ALTER TABLE dbo.MyTable ALTER COLUMN CharCol
varchar(10)COLLATE Latin1_General_CI_AS NOT NULL;

How to get ratio of 2 field data

I want to get the ratio of 2 field data from mysql
Example i have this data:
width height
1024 | 960
2880 | 1800
1440 | 900
I want to get the ratio of these data(example 4:3, 16:9, etc.)
so the output should be:
width height
4 | 3
8 | 5
8 | 5
Anyone can help me?
the problem is then to reduce a fraction (witdth/height) to its simplest form, in which case we can call a function to give us the Greatest Common Factor between those two numbers to divide into:
select width, height, width/gcd(width,height) wratio,
height/gcd(width,height) hratio
from wh
Function gcd:
CREATE FUNCTION gcd(x int, y int) RETURNS int DETERMINISTIC
BEGIN
DECLARE dividend int;
DECLARE divisor int;
DECLARE remainder int;
SET dividend := GREATEST(x, y);
SET remainder := LEAST(x, y);
WHILE remainder != 0 DO
SET divisor = remainder;
SET remainder = MOD(dividend, divisor);
SET dividend = divisor;
END WHILE;
RETURN divisor;
END
Note: I found the gcd function here: http://thenoyes.com/littlenoise/?p=143
tested in sqlfiddle
That works:
SELECT * , (
width / IF(height=0,1,height)
) AS ratio
FROM `my_table`
But be careful: do not divide by 0

Mysql Spatial Extension. How to check if Linestring CONTAINS a Point?

I'm trying to determine if Linestring have Point.... f.e.
SET ls = geomfromtext('LINESTRING(1 0,3 0)');
SET p = geomfromtext('POINT(2 0)');
if i do CONTAINS(ls,p) i have true. but there is no point (2 0) in line
i need exactly containing. is there any function for it?
i tried all functions from documentation and non of them do what i need.
f.e.
SELECT ASTEXT(path) FROM paths WHERE INTERSECTS(path, GEOMFROMTEXT('POINT(604 0)'))
gives "wrong" results
LINESTRING(572 0,600 0,601 0,602 0,603 0,604 0,605 0,606 0,607 0,608 0,402 0)
LINESTRING(402 0,609 0,610 0,611 0,612 0,613 0,614 0,615 0,616 0,617 0,618 0,619 0,620 0,621 0,622 0,623 0)
LINESTRING(359 0,449 0,801 0,422 0,802 0,803 0,498 0)
LINESTRING(572 0,795 0,796 0,797 0,798 0,799 0,800 0,345 0,359 0)
LINESTRING(792 0,768 0,793 0,794 0,572 0)
LINESTRING(342 0,904 0,905 0,906 0)
LINESTRING(912 0,914 0,915 0,916 0,341 0)
LINESTRING(344 0,917 0,918 0,919 0,920 0,800 0)
LINESTRING(918 0,922 0,923 0,924 0,925 0,926 0,927 0,343 0)
LINESTRING(940 0,947 0,948 0,949 0,604 0)
MBRWITHIN gives the same result
i wrote a function, but it is very-very slow:
FUNCTION `IDIL`(`id` INT, `line` LINESTRING) RETURNS INT(1)
NO SQL
DETERMINISTIC
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE p1X INT(20);
DECLARE p1Y INT(20);
DECLARE p1 POINT;
DECLARE i INT DEFAULT 0;
DECLARE result INT(1) DEFAULT 0;
SET n = NUMPOINTS(line);
WHILE i<n DO
SET p1 = POINTN(line, (i+1));
SET p1X = X(p1);
SET p1Y = Y(p1);
IF p1X=id OR p1Y=id THEN RETURN 1; END IF;
SET i = i + 1;
END WHILE;
RETURN result;
END$$
Could you please check out the following reference on this article
SQLFIDDLE
SET #ls = 'LineString(1 0,3 0)';
SET #xs = geomfromtext(#ls);
SET #p = geomfromtext('POINT(2 0)');
SELECT MBRWithin(#xs,#p);
Sorry I have given the wrong link.
results
MBRWITHIN(#XS,#P)
0
Looking at your line 1 0, 3 0 --> 2 0 point exists on it.

Implementing parts of rfc4226 (HOTP) in mysql

Like the title says, I'm trying to implement the programmatic parts of RFC4226 "HOTP: An HMAC-Based One-Time Password Algorithm" in SQL. I think I've got a version that works (in that for a small test sample, it produces the same result as the Java version in the code), but it contains a nested pair of hex(unhex()) calls, which I feel can be done better. I am constrained by a) needing to do this algorithm, and b) needing to do it in mysql, otherwise I'm happy to look at other ways of doing this.
What I've got so far:
-- From the inside out...
-- Concatinate the users secret, and the number of time its been used
-- find the SHA1 hash of that string
-- Turn a 40 byte hex encoding into a 20 byte binary string
-- keep the first 4 bytes
-- turn those back into a hex represnetation
-- convert that into an integer
-- Throw away the most-significant bit (solves signed/unsigned problems)
-- Truncate to 6 digits
-- store into otp
-- from the otpsecrets table
select (conv(hex(substr(unhex(sha1(concat(secret, uses))), 1, 4)), 16, 10) & 0x7fffffff) % 1000000
into otp
from otpsecrets;
Is there a better (more efficient) way of doing this?
I haven't read the spec, but I think you don't need to convert back and forth between hex and binary, so this might be a little more efficient:
SELECT (conv(substr(sha1(concat(secret, uses)), 1, 8), 16, 10) & 0x7fffffff) % 1000000
INTO otp
FROM otpsecrets;
This seems to give the same result as your query for a few examples I tested.
This is absolutely horrific, but it works with my 6-digit OTP tokens. Call as:
select HOTP( floor( unix_timestamp()/60), secret ) 'OTP' from SecretKeyTable;
drop function HOTP;
delimiter //
CREATE FUNCTION HOTP(C integer, K BINARY(64)) RETURNS char(6)
BEGIN
declare i INTEGER;
declare ipad BINARY(64);
declare opad BINARY(64);
declare hmac BINARY(20);
declare cbin BINARY(8);
set i = 1;
set ipad = repeat( 0x36, 64 );
set opad = repeat( 0x5c, 64 );
repeat
set ipad = insert( ipad, i, 1, char( ascii( substr( K, i, 1 ) ) ^ 0x36 ) );
set opad = insert( opad, i, 1, char( ascii( substr( K, i, 1 ) ) ^ 0x5C ) );
set i = i + 1;
until (i > 64) end repeat;
set cbin = unhex( lpad( hex( C ), 16, '0' ) );
set hmac = unhex( sha1( concat( opad, unhex( sha1( concat( ipad, cbin ) ) ) ) ) );
return lpad( (conv(hex(substr( hmac, (ascii( right( hmac, 1 ) ) & 0x0f) + 1, 4 )),16,10) & 0x7fffffff) % 1000000, 6, '0' );
END
//
delimiter ;

MYSQL: self written string-manipulation function returns unexpected result

I'm trying to implement a MYSQL function MY_LEFT_STR(STRING x,INT position) in such a way that
MY_LEFT_STR('HELLO', 4) => returns 'HELL' (same as internal LEFT function)
MY_LEFT_STR('HELLO',-1) => returns 'HELL'
DROP FUNCTION IF EXISTS MY_LEFT_STR;
CREATE FUNCTION MY_LEFT_STR(
in_str VARCHAR(255),
pos INT
)
RETURNS VARCHAR(255)
BEGIN
IF (pos < 0) THEN
RETURN LEFT(in_str,LENGTH(in_str) - pos);
ELSE
RETURN LEFT(in_str,pos);
END IF;
END;
the result is
select left_str('HELLO', 4) as A
, left_str('HELLO',-1) as B
, left('HELLO',length('HELLO')-1) as C
from dual
+-----+-----+-----+
| A | B | C |
+-----+-----+-----+
|HELL |HELLO|HELL |
+-----+-----+-----+
QUESTION What is wrong with my function declaration? (Besides a generall lack of testing for bordercases like MY_LEFT_STR('A',-4) ...
ANSWER: so embarassing ... the answer lies in the double negative for pos=-1 in
RETURN LEFT(in_str,LENGTH(in_str) - pos);
this should be
RETURN LEFT(in_str,LENGTH(in_str) + pos);
Here's a clue: What's the result of LENGTH(in_str) - (-1)?
When pos is negative, then LENGTH(in_str) - pos yields a number longer than the length of the string. So LEFT() is bound to return the whole string, because you're asking for more characters than the total length of the string.
RETURN LEFT(in_str,LENGTH(in_str) - pos);
If pos is negative, won't LENGTH(in_str) - pos give you (for your example):
LENGTH(HELLO) - (-1) = 6?