How to draw triangles in SQL Server as shown below?
I want to implement it by using WHILE loops, but I am unable to print 20 '*' in a single line in SQL Server.
How can I achieve this?
Use REPLICATE inside a WHILE. I think, you can achieve your desired output if you do it correctly?
DECLARE #i INT = 20
WHILE(#i>0)
BEGIN
PRINT REPLICATE('* ', #i);
SET #i = #i - 1;
END
You can use REPLICATE to repeat a character a certain number of times. To generate a sequence of numbers from 1 to 20 you don't need a WHILE anyway - SQL doesn't really need the WHILE statement to work with data.
Number sequences are always useful which is why almost every SQL developer creates a Numbers table.
If you don't already have one, a quick and dirty way to generate 20 numbers is to select the top 20 rows from a systems table, and use ROW_NUMBER to calculate row numbers eg:
select top 20 replicate('*',21-row_number() over (order by id) )
from sys.sysobjects
With a Numbers table, the query is simpler:
select replicate('*',Number )
from dbo.Numbers
where Numbers.Number <= 20
order by Number desc
Numbers tables are extremely useful, eg for sets of elements like 200 days starting from 2017/1/1 :
select dateadd(d,Number,cast('20170101' as date))
from dbo.Numbers
where Numbers.n<= 20
order by Number desc
Try This,
DECLARE #StrLen INT = 20
WHILE #StrLen >= 1
BEGIN
PRINT REPLICATE('*',#StrLen)
SET #StrLen = #StrLen - 1
END
Without replicate() solution
BEGIN
DECLARE #i int = 20
DECLARE #j int
DECLARE #line varchar(max)
WHILE #i > 0
BEGIN
SET #line = ''
SET #j = 0
WHILE #j < #i
BEGIN
SET #line += '* '
SET #j = #j + 1
END
PRINT #line
SET #i = #i - 1
END
END
Using recursive CTE (Press Ctrl+T for results in text)
;WITH A AS (
SELECT REPLICATE('* ', 20) X
UNION ALL
SELECT LEFT(X, LEN(X) - 2)
FROM A
WHERE X > '* '
)
SELECT * FROM A
Another way:
;WITH A AS (
SELECT 20 X
UNION ALL
SELECT X - 1 FROM A
WHERE X > 1
)
SELECT REPLICATE('* ', X) FROM A
Related
I am working with SQL Server and have come to encounter a situation for which I have no answer. It is as follows:
Given a string diuuuu, for example, I would like this output diu. The logic behind it is the last occurrance of each digit (d, i, u) in order. Other examples:
diudi = udi
dididi = di
A Regex solution would be greatly appreciated. Thank you!
This isn't particularly pretty, but it does do the job:
WITH VTE AS(
SELECT *
FROM (VALUES('diudi'),('diuuuu'),('dididi'),('ddddd'))V(YourColumn)),
Ranks AS(
SELECT V.YourColumn,
CI.C,
CI.I,
RANK() OVER (PARTITION BY V.YourColumn ORDER BY CI.I) AS R
FROM VTE V
CROSS APPLY (VALUES(REVERSE(V.YourColumn)))R(YourColumn)
CROSS APPLY (VALUES('d',CHARINDEX('d',R.YourColumn)),
('i',CHARINDEX('i',R.YourColumn)),
('u',CHARINDEX('u',R.YourColumn)))CI(C,I))
SELECT YourColumn,
REVERSE(CONCAT(MAX(CASE WHEN R = 1 AND I > 0 THEN C END),MAX(CASE WHEN R = 2 AND I > 0 THEN C END),MAX(CASE WHEN R = 3 AND I > 0 THEN C END)))
FROM Ranks
GROUP BY YourColumn;
This should work:
(.)(?!.*\1)
The groups returned, concatenated together, give you what you want.
Here's a SQL Answer:
CREATE FUNCTION dbo.udf_LastChars(#str VARCHAR(100))
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #result VARCHAR(100)
DECLARE #char CHAR(1)
DECLARE #i INT = 1
WHILE (#i <= LEN(#str))
BEGIN
SET #char = SUBSTRING(#str, #i, 1)
SET #result = CONCAT(REPLACE(#result, #char, ''), #char)
SET #i = #i + 1
END
RETURN #result
END
This query converts dynamic int to binary, but I want to split into rows.
declare #value int
set #value = 96
declare #result varchar(32)
set #result = ''
while 1 = 1
begin
select
#result = convert(char(1), #value % 2) + ',' +#result,
#value = convert(int, #value / 2)
if #value = 0 break
end
select substring(#result, 1, len(#result)-1) as result
Please help me to find a solution.
This is the result of my query.
1,1,0,0,0,0,0
My question is: how can I split this result into rows from right to left?
My result will need to be this (I'm trying to insert into a #table):
0
0
0
0
0
1
1
Thanks
Using a WHILE seems like a really bad idea. If you want to achieve what you have this would be a far faster solution:
DECLARE #I int = 96
SELECT CONVERT(bit,#i & V.b)
FROM (VALUES(1),(2),(4),(8),(16),(32),(64)) V(b)
ORDER BY V.b;
I would like to insert a value retrieved from a counter in SQL and repeat it 300 times.
Something like:
DECLARE #Counter = 0;
-- BEGIN Loop
SET #Counter = #Counter + 1
INSERT INTO tblFoo VALUES(#Counter)
-- REPEAT 300 times
How can I achieve this?
Thanks
You may try it like this:
DECLARE #i int = 0
WHILE #i < 300
BEGIN
SET #i = #i + 1
/* your code*/
END
DECLARE #first AS INT = 1
DECLARE #last AS INT = 300
WHILE(#first <= #last)
BEGIN
INSERT INTO tblFoo VALUES(#first)
SET #first += 1
END
I would prevent loops in general if i can, set approaches are much more efficient:
INSERT INTO tblFoo
SELECT TOP (300) n = ROW_NUMBER()OVER (ORDER BY [object_id])
FROM sys.all_objects ORDER BY n;
Demo
Generate a set or sequence without loops
In ssms we can use GO to execute same statement
Edit
This mean if you put
some query
GO n
Some query will be executed n times
Found some different answers that I combined to solve simulair problem:
CREATE TABLE nummer (ID INTEGER PRIMARY KEY, num, text, text2);
WITH RECURSIVE
for(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM for WHERE i < 1000000)
INSERT INTO nummer SELECT i, i+1, "text" || i, "otherText" || i FROM for;
Adds 1 miljon rows with
id increased by one every itteration
num one greater then id
text concatenated with id-number like: text1, text2 ... text1000000
text2 concatenated with id-number like: otherText1, otherText2 ... otherText1000000
i'm pretty new to SQL and stored procedures and i'm a bit stuck - so any help would be appreciated
how do i loop through each row and assign it the random value i'm generating?
Here is my Storedproc:
CREATE PROCEDURE StoredProc8
AS
BEGIN
DECLARE #total INT
DECLARE #Count INT = 0
DECLARE #Random INT = 0
SELECT #total = COUNT(CustomerID) FROM Customers
WHILE(#Count<= #total)
BEGIN
SELECT #Random = 2 * RAND()
EXEC ('update Customers set col1= ' + #Random )
SELECT #Count = #Count+1
END
END
If you simple need to assign 0 or 1 randomly - you can use RAND() with random seed:
UPDATE Customers SET COL1 = RAND(CHECKSUM(NEWID()))*2
Demo: http://sqlfiddle.com/#!3/31699/9
I found a few threads on this using the search feature, but nothing for a purely T-SQL solution.
the need - A system is storing a weekly schedule as 0's and 1's in a string format to represent a week. 1 means yes, 0 means no....so 1100111 means sunday yes (first one), Monday yes (second 1), Tuesday no (the 0)...etc.
Short question - How do I go from an ascii char such as '>' to it's hex code '3E' and ultimately to it's binary '00111110' representation?
Long question - I'm extracting from a flat file system that stores a table as:
ID int,
priority_1 varchar(2)
...
It actually goes to priroity_128 (silly flat file), but I'm only interested in 1-7 and the logic for one should be easily reused for the others. I unfortunately have no control over this part of the extract. The values I get look like:
1 >
2 (edit, I actually put a symbol here that I receive from the system but the forum doesn't like.)
3 |
4 Y
I get the feeling these are appearing as their ascii chars because of the conversion as I extract.
select convert(varbinary,'>',2)
This returns 0x3E. The 0x part can be ignored... 3 in binary is 0011 and E is 1110...3E = 00111110. Trim the first 0 and it leaves the 7 bit code that I'm looking for. Unfortunately I have no idea how to express this logic here in T-SQL. Any ideas? I'm thinking as a function would be easiest to use...something like:
select id, binaryversionof(priority_1)
Here's a UDF that will convert from base-10 to any other base, including base-2...
Here's how you can use it:
SELECT YourDatabase.dbo.udf_ConvertFromBase10(convert(varbinary, '>', 2), 2)
Here's what it returns:
111110
And here's the function definition:
CREATE FUNCTION [dbo].[udf_ConvertFromBase10]
(
#num INT,
#base TINYINT
)
RETURNS VARCHAR(255)
AS
BEGIN
-- Check for a null value.
IF (#num IS NULL)
RETURN NULL
-- Declarations
DECLARE #string VARCHAR(255)
DECLARE #return VARCHAR(255)
DECLARE #finished BIT
DECLARE #div INT
DECLARE #rem INT
DECLARE #char CHAR(1)
-- Initialize
SELECT #string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SELECT #return = CASE WHEN #num <= 0 THEN '0' ELSE '' END
SELECT #finished = CASE WHEN #num <= 0 THEN 1 ELSE 0 END
SELECT #base = CASE WHEN #base < 2 OR #base IS NULL THEN 2 WHEN #base > 36 THEN 36 ELSE #base END
-- Loop
WHILE #finished = 0
BEGIN
-- Do the math
SELECT #div = #num / #base
SELECT #rem = #num - (#div * #base)
SELECT #char = SUBSTRING(#string, #rem + 1, 1)
SELECT #return = #char + #return
SELECT #num = #div
-- Nothing left?
IF #num = 0 SELECT #finished = 1
END
-- Done
RETURN #return
END
Your solution returns a string of a variable length. Not sure whether it was by design or you simply overlooked that fact.
Anyway, here's my solution, which always returns 7 0s or 1s:
CREATE FUNCTION fnIntTo7Bits (#Value int)
RETURNS varchar(7)
AS BEGIN
DECLARE #Bits varchar(7);
SELECT #Bits = COALESCE(#Bits, '') + CAST(CAST(#Value & number AS bit) AS varchar)
FROM master..spt_values
WHERE type = 'P' AND number IN (1, 2, 4, 8, 16, 32, 64)
ORDER BY number DESC;
RETURN #Bits;
END;
The master..spt_values table is a system table used internally but also accessible to the user. It seems to have been inherited from Sybase so it's a very old tool, which, to my mind, means it won't go too soon.
But if you like, you can use your own number table, which you don't even have to materialise, like this:
...
SELECT #Bits = COALESCE(#Bits, '') + CAST(CAST(#Value & number AS bit) AS varchar)
FROM (
SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 4 UNION ALL SELECT 8 UNION ALL
SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 64
) s (number)
ORDER BY number DESC;
...
Answering my own question...though curious if anyone has something more elegant. I found this unsourced function using google:
CREATE FUNCTION udf_bin_me (#IncomingNumber int)
RETURNS varchar(200)
as
BEGIN
DECLARE #BinNumber VARCHAR(200)
SET #BinNumber = ''
WHILE #IncomingNumber <> 0
BEGIN
SET #BinNumber = SUBSTRING('0123456789', (#IncomingNumber % 2) + 1, 1) + #BinNumber
SET #IncomingNumber = #IncomingNumber / 2
END
RETURN #BinNumber
END
Then use the Ascii function to get the char to it's ascii decimal value:
select dbo.udf_bin_me(ascii('>'))
Seems to be a bit of a run around, but I can work from that. Better solution anyone?
I just whipped this up, it maybe buggy... but it works:
DECLARE #value INT, #binary VARCHAR(10)
SELECT #value = ASCII('m'), #binary = ''
;WITH [BINARY] ([Location], [x], [BIT])
AS
(
-- Base case
SELECT 64, #value, #value % 2
UNION ALL
-- Recursive
SELECT [BINARY].[Location] / 2, [BINARY].[x] / 2, ([BINARY].[x] / 2) % 2
FROM [BINARY]
WHERE [BINARY].[Location] >= 2
)
SELECT #binary = CAST([BIT] AS CHAR(1)) + #binary FROM [BINARY]
SELECT #binary