I using this loop to print number
DECLARE #A Int
SET #A = 33
WHILE #A < 55
BEGIN
SELECT #A as sequence
SET #A = #A + 1
END
GO
But problem that with every loop message is printed like example:
sequence
-----------
53
(1 row(s) affected)
How to print in order like this:
34
35
36
37
Can help me with CTE example for this?
Use PRINT
DECLARE #A INT
SET #A = 33
WHILE #A < 55
BEGIN
PRINT #A
SET #A = #A + 1
END
GO
For the CTE you can try
DECLARE #A INT,
#End INT
SET #A = 33
SET #End = 55
;WITH Selected AS (
SELECT #A Val
UNION ALL
SELECT Val + 1
FROM Selected
WHERE Val < #End
)
SELECT *
FROM Selected
If all you want is to print the value, you can use the PRINT statement. If you want to actually return the result (if your code is part of a stored procedure, for example), you could define a temporary table type variable, insert data on it on each loop, then return the contents of the table.
Related
DECLARE #a int
SET #a = 5
SET #a = CASE
WHEN #a >= 5 THEN 1
WHEN #a <= 5 THEN 2
WHEN a < 10 THEN 3
END
SELECT #a
#a value should be 3, but why it is showing 1?
Can anyone explain how it works?
I have been asked same question one of SQL interview
Case expression will Evaluates, as order you specified :
So, you want prioritized when clause :
case when #a < 10 then 3
when #a <= 5 then 2
when #a >= 5 then 1
end
The CASE statement goes through conditions and returns a value when the first condition is met. Following two way of your problem should be solved, you can try one of them.
declare #a int
set #a=5
set #a= case when #a>5 then 1
when #a<5 then 2
when #a <10 then 3 end
select #a
set #a=5
set #a= case when #a <10 then 3
when #a>=5 then 1
when #a<=5 then 2 end
select #a
Sample table
Record Number | Filter | Filters_Applied
----------------------------------------------
1 | yes | red, blue
2 | yes | green
3 | no |
4 | yes | red, red, blue
Is it possible to query all records where there are duplicate string values? For example, how could I query to pull record 4 where the string "red" appeared twice? Except in the table that I am dealing with, there are far more string values that can populate in the "filters_applied" column.
CLARIFICATION I am working out of Periscope and pulling data using SQL.
I assume that you have to check that in the logical page.
You can query the table with like '%red%'.
select Filters_Applied from table where Filters_Applied like '%red%';
You will get the data which has red at least one. Then, doing some string analysis in logic page.
In php, You can use the substr_count function to determine the number of occurrences of the string.
//the loop to load db query
while(){
$number= substr_count("Filters_Applied",red);
if($number>1){
echo "this".$Filters_Applied.">1"
}
}
for SQL-SERVER or other versions which can run these functions
Apply this logic
declare #val varchar(100) = 'yellow,blue,white,green'
DECLARE #find varchar(100) = 'green'
select #val = replace(#val,' ','') -- remove spaces
select #val;
select (len(#val)-len(replace(#val,#find,'')))/len(#find) [recurrence]
Create this Function which will parse string into rows and write query as given below. This will works for SQL Server.
CREATE FUNCTION [dbo].[StrParse]
(#delimiter CHAR(1),
#csv NTEXT)
RETURNS #tbl TABLE(Keys NVARCHAR(255))
AS
BEGIN
DECLARE #len INT
SET #len = Datalength(#csv)
IF NOT #len > 0
RETURN
DECLARE #l INT
DECLARE #m INT
SET #l = 0
SET #m = 0
DECLARE #s VARCHAR(255)
DECLARE #slen INT
WHILE #l <= #len
BEGIN
SET #l = #m + 1--current position
SET #m = Charindex(#delimiter,Substring(#csv,#l + 1,255))--next delimiter or 0
IF #m <> 0
SET #m = #m + #l
--insert #tbl(keys) values(#m)
SELECT #slen = CASE
WHEN #m = 0 THEN 255 --returns the remainder of the string
ELSE #m - #l
END --returns number of characters up to next delimiter
IF #slen > 0
BEGIN
SET #s = Substring(#csv,#l,#slen)
INSERT INTO #tbl
(Keys)
SELECT #s
END
SELECT #l = CASE
WHEN #m = 0 THEN #len + 1 --breaks the loop
ELSE #m + 1
END --sets current position to 1 after next delimiter
END
RETURN
END
GO
CREATE TABLE Table1# (RecordNumber int, [Filter] varchar(5), Filters_Applied varchar(100))
GO
INSERT INTO Table1# VALUES
(1,'yes','red, blue')
,(2,'yes','green')
,(3,'no ','')
,(4,'yes','red, red, blue')
GO
--This query will return what you are expecting
SELECT t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)), count(*)NumberOfRows
FROM Table1# t
CROSS APPLY dbo.StrParse (',', t.Filters_Applied)
GROUP BY t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)) HAVING count(*) >1
You didn't state your DBMS, but in Postgres this isn't that complicated:
select st.*
from sample_table st
join lateral (
select count(*) <> count(distinct trim(item)) as has_duplicates
from unnest(string_to_array(filters_applied,',')) as t(item)
) x on true
where x.has_duplicates;
Online example: http://rextester.com/TJUGJ44586
With the exception of string_to_array() the above is actually standard SQL
I came with a problem of sorting using ORDER BY. I found a lot of similar questions, but no answer fits my needs. The task is:
I have column [LABEL] which contains strings, and i want to get an order like this:
label
'1'
'2'
'11R'
'11T9'
'11T10'
'RT_5'
'RT_6'
'RT_10'
'RT_10b'
'RT_10dyn'
and so on...
instead of:
'1'
'11R'
'11T10'
'11T9'
'2S'
'RT_10'
'RT_10b'
'RT_10dyn'
'RT_5'
'RT_6'
the label columb might be like any combination of characters.
The problem is to find numbers in names, and if it is possible to sort by those numbers, then by other charaters...
After a few hours here is the solution:
I created a function to change the labels in specific way:
Each NUMBER in the input #in is replaced by the same number
writen in #digits chars WITH leadings zeros.
For example:
#digit = 4, #in = 'aa300bb' return = '_aa0300bb_'.
#digit = 5, #in = 'aa300bb' return = '_aa00300bb_'.
#digit = 3, #in = 'a2c4e5' return = '_a002c004e005_'.
And here is the function:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fnMixSort]')
AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fnMixSort]
GO
CREATE FUNCTION [dbo].[fnMixSort] (
#in NVARCHAR(250),
#digits int
) RETURNS NVARCHAR(1000) AS
BEGIN
DECLARE
#starts int,
#i int, -- position where next NUMBER starts
#j int, -- position where next NUMBER ends
#temp nvarchar(1000)
set #starts = 1
set #in = '_' + #in + '_' -- extended LABEL: protection from EMPTY input
while (1=1)
begin
select #temp = substring(#in, #starts, len(#in))
-- #i #j - start/end position of first number
SELECT #i = COALESCE( PATINDEX('%[0-9]%',#temp ), 0)
SELECT #j = COALESCE( PATINDEX('%[0-9][^0-9]%',#temp ), 0)
if #i = 0 break -- no more NUMBERs in the LABEL
-- now we PUT at posiotion=#i+#start-1 specific numbers of '0'
select #in = STUFF(#in, #i + #starts - 1, 0, REPLICATE('0', #digits-#j+#i-1))
select #starts = #starts + #i + #digits - 1
end
-- -------- return ---------
RETURN #in
END
GO
lets create some table to check the function:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[aaaa_test]')
AND type in (N'U'))
DROP TABLE [dbo].[aaaa_test]
GO
CREATE TABLE [dbo].[aaaa_test](
Label [varchar](255) NULL
)
INSERT INTO [dbo].[aaaa_test] ([Label])
VALUES ('bb'),('aa12'),(''),('30'),('10rt'),
('12ru'),('1rt'),('9rt'),('aa8'),('aa10'),('aa'),
('12rz'),('12rt'),('9rt5'),('9_rt_10_23'),('9_rt_10_5'),('9rt12'),
('12rz34'),('12rz3'),('12rz35c'),('12rz105b'),('12rt'),('9rt5'),('9rt10'),('9rt12')
select
[label]
,dbo.fnMixSort(Label,5) as [fnMixSort_returns]
from [dbo].[aaaa_test]
order by dbo.fnMixSort(Label,5)
And the result
label fnMixSort_returns
----------------------------------
1rt _00001rt_
9_rt_10_5 _00009_rt_00010_00005_
9_rt_10_23 _00009_rt_00010_00023_
9rt _00009rt_
9rt5 _00009rt00005_
9rt5 _00009rt00005_
9rt10 _00009rt00010_
9rt12 _00009rt00012_
9rt12 _00009rt00012_
10rt _00010rt_
12rt _00012rt_
12rt _00012rt_
12ru _00012ru_
12rz _00012rz_
12rz3 _00012rz00003_
12rz34 _00012rz00034_
12rz35c _00012rz00035c_
12rz105b _00012rz00105b_
30 _00030_
aa _aa_
aa8 _aa00008_
aa10 _aa00010_
aa12 _aa00012_
bb _bb_
it was my first time to post here...
hope it will help someone oneday..
You can substr [LABEL] column into different columns and then order by those columns. As null is sorted first you don't need to do anything extra for values with less character.
How ever you can also follow this thread here.
Here in this solution the logic is :-
If ID is numeric, add 21 '0's in front of the ID value and get the last 20 characters.
If ID is not numeric, add 21 ‘’s at the end of the ID value and get the first 20 characters.
Or this is a better solution for you query Sort Alphanumeric value
Let us see if it helps.
ANOTHER SOLUTION: different exchanged_label:
/** ==========================================================
FUNCTION DESCRIPTION
-------------------------------------------------------------
Function for special sorting - natural-mix sorting.
Order by : number in word are treated as number, not as a
characters only.
So 'a2' is before 'a10' and '9R' is before '10R' ...
-------------------------------------------------------------
Function puts special prefix before each number.
If number has 1 digit -> with prefix is 0A
If number has 2 digits -> with prefix is 0B
... ... ...
If number has 16 digits -> with prefix is 0P
If number has 17 digits -> with prefix is 0PA
If number has 18 digits -> with prefix is 0PB
... ... ...
If number has 32 digits -> with prefix is 0PP
If number has 33 digits -> with prefix is 0PPA
... and so on...
For example:
aa123bb9 -> aa0C123bb0A9
**/
CODE
CREATE FUNCTION [dbo].[fnMixSort] ( #in NVARCHAR(1000) ) RETURNS NVARCHAR(1000) AS
BEGIN
DECLARE
#starts int,
#i int, -- position where next NUMBER starts
#j int, -- position where next NUMBER ends
#temp nvarchar(1000)
set #starts = 1
set #in = '_' + #in + '_' -- extended LABEL: protection from EMPTY input
while (1=1)
begin
select #temp = substring(#in, #starts, len(#in))
SELECT #i = COALESCE( PATINDEX('%[0-9]%',#temp ), 0)
if #i = 0 break -- no more NUMBERs in the LABEL
SELECT #j = COALESCE( PATINDEX('%[0-9][^0-9]%',#temp ), 0)
select #temp = '0' -- numbers->must still be numbers: before letters
while (#j >= #i + 16)
begin
select #j = #j - 16
select #temp = #temp + 'P'
end
select #temp = #temp + CHAR(#j - #i + 65) -- char(65) is 'A'
select #in = STUFF(#in, #i + #starts - 1, 0, #temp)
select #starts = #starts + LEN(#temp) + (LEN(#temp)-2)*16 + #j
end -- while
RETURN #in
END
GO
results:
1rt _0A1rt_
9_rt_10_5 _0A9_rt_0B10_0A5_
9_rt_10_23 _0A9_rt_0B10_0B23_
9rt _0A9rt_
9rt5 _0A9rt0A5_
9rt5 _0A9rt0A5_
9rt10 _0A9rt0B10_
9rt12 _0A9rt0B12_
9rt12 _0A9rt0B12_
10rt _0B10rt_
12rt _0B12rt_
12rt _0B12rt_
12ru _0B12ru_
12rz _0B12rz_
12rz3 _0B12rz0A3_
12rz34 _0B12rz0B34_
12rz105b _0B12rz0C105b_
30 _0B30_
9234567890123456123456789012345rz38c _0PO9234567890123456123456789012345rz0B38c_
12345678901234561234567890123456rz35c _0PP12345678901234561234567890123456rz0B35c_
123456789012345612345678901234561rz36c _0PPA123456789012345612345678901234561rz0B36c_
aa _aa_
aa0A _aa0A0A_
aa0b _aa0A0b_
aa8 _aa0A8_
aa10 _aa0B10_
aa12 _aa0B12_
bb _bb_
Same approach as pi.314 but rewrite for PostgreSQL:
CREATE OR REPLACE FUNCTION fnNumberAwareSort(value varchar, digits integer)
RETURNS varchar
AS '
DECLARE
numbers VARCHAR[];
texts VARCHAR[];
BEGIN
value = CONCAT(''_'', value, ''_'');
SELECT ARRAY(SELECT res[1] FROM regexp_matches(value, ''\d+'', ''g'') AS res) INTO numbers;
texts = regexp_split_to_array(value, ''\d+'');
FOR i IN 1..array_upper(texts,1) LOOP
numbers[i] = lpad(numbers[i], digits, ''0'');
END LOOP;
value = texts[1];
FOR i IN 2..array_upper(texts,1) LOOP
value = value || numbers[i-1] || texts[i];
END LOOP;
RETURN value;
END;
' LANGUAGE plpgsql;
I have a simple while loop and in the loop I am declaring a DECLARE #TABLE with one column and its data type is id. While the loop loops the records I am just inserting the values into the table variable. Whenever the loop is looping it the Declare #TABLE should get recreated and the old values should not exist. But its not happening. Below is the code
Declare #V int
Set #V = 1
While (#V <= 3)
begin
DECLARE #Changes table
(
Id int
)
Insert into #Changes
Values (#V)
select * from #Changes
SET #V=#V+1
END
In normal we should get the output as
1
2
3
But the output i am getting is
1
1
2
1
2
3
Which is wrong.
Is this the normal behavior or a bug in SQL
That's normal. T-SQL is a very odd language. Variable declarations affect when a variable can be referenced, but they don't actually participate in control flow.
Consider:
IF 1 = 0
BEGIN
DECLARE #a int
END
SET #a = 1
PRINT #a
Actually prints 1. It doesn't complain about an undeclared variable.
As Allan notes in the comments, if you want #Changes to be empty at the start of each loop, make it so:
Declare #V int
DECLARE #Changes table
(
Id int
)
Set #V = 1
While (#V <= 3)
begin
delete from #Changes
Insert into #Changes
Values (#V)
select * from #Changes
SET #V=#V+1
END
My question here is how do I use a variable to be a column name in a select statement. I have created the variable #B to be a column name that is BGNDATE1 through BGNDATE12. Rather than have 12 select statements I created a while loop. The column name is basically BGNDATE + the incremented integer.
The error I am getting is:
Conversion failed when converting the varchar value 'BGNDATE1' to data type int.
USE X --this is the database
DECLARE #DATES TABLE (ROWID INT, FISCDATES INT)
DECLARE #FY INT = 2012
DECLARE #I INT
DECLARE #IV VARCHAR(2)
DECLARE #B VARCHAR(9)
SELECT #FY AS FY
SET #I = 1
WHILE #I <= 12
BEGIN
SET #IV = #I
SET #B = 'BGNDATE' + #IV
INSERT INTO #DATES (ROWID)
SELECT #I
MERGE INTO #DATES AS T
USING (
--This is where the error is with regards to the variable #B
SELECT #B AS FISCDATES FROM DBO.Y -- Y is the table in the database
WHERE FSCYEAR = #FY) AS S
ON T.ROWID = #I
WHEN MATCHED
THEN UPDATE
SET T.FISCDATES = S.FISCDATES;
SET #I = #I + 1
END
SELECT * FROM #DATES
You can't use a variable as a column name (unless you create the entire query dynamically), but you can use a variable to select from different columns:
...
SELECT
CASE #IV
WHEN 1 THEN BGNDATE1
WHEN 2 THEN BGNDATE2
WHEN 3 THEN BGNDATE3
WHEN 4 THEN BGNDATE4
WHEN 5 THEN BGNDATE5
WHEN 6 THEN BGNDATE6
WHEN 7 THEN BGNDATE7
WHEN 8 THEN BGNDATE8
WHEN 9 THEN BGNDATE9
WHEN 10 THEN BGNDATE10
WHEN 11 THEN BGNDATE11
WHEN 12 THEN BGNDATE12
END AS FISCDATES FROM DBO.Y
...
When you select #B that won't work because #B is not a column name, it is a variable.
It would be best to denormalize the table so instead of having 12 columns named BGNDATE 1 through 12 you had another table to join to.
If you can't do that, do it with dynamic sql:
exec('MERGE INTO #DATES AS T
USING (
SELECT ' + #B + ' AS FISCDATES FROM DBO.Y
WHERE FSCYEAR = #FY) AS S
ON T.ROWID = #I
WHEN MATCHED
THEN UPDATE
SET T.FISCDATES = S.FISCDATES;')