Looping within SQL(ite) SELECT - sql

The following SQLite query works:
SELECT Name,
(CASE
WHEN P1=1 THEN 1
WHEN P2=1 THEN 2
WHEN P3=1 THEN 3
WHEN P4=1 THEN 4
WHEN P5=1 THEN 5
ELSE NULL
END) AS Col
FROM table
but is there a way to loop then WHEN statements? Following this question and answer I tried:
SELECT Nachname, Vorname,
(CASE
DECLARE #i int = 0
WHILE #i < 5 BEGIN
SET #i = #i + 1
WHEN P#i=1 THEN #i
END
END) AS Col
FROM table
but this didn't work, saying: error in statement: near "#i": syntax error.
For full information I am applying this statement through a sqldf function on R.

SQLite has no mechanism to create column names dynamically.
You have to list all columns by hand, or create the SQL query dynamically from your program.

Related

Iterate through query

I'm trying to iterate through simple query
select a_id, count(ID), TIMESTAMPDIFF(SECOND,MIN(`Time`),MAX(`Time`))
from data
where a_id = 1
This is what I managed to create
DECLARE counter INT DEFAULT 0;
WHILE counter < 10
BEGIN
select a_id, count(ID), TIMESTAMPDIFF(SECOND,MIN(`Time`),MAX(`Time`))
from data
where a_id = counter
SET counter = counter + 1;
END
But I get:
Error occurred during SQL script execution
Reason:
SQL Error [1064] [42000]: (conn=1956) You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DECLARE counter INT DEFAULT 0' at line 1
You don't need a while loop for this. You can do this in a single query. One method is:
with recursive ids as (
select 1 as id
union all
select id + 1
from ids
where id < 10
)
select d.a_id, count(*)
timestampdiff(second, min(d.`Time`), min(d.`Time`))
from ids left join
data d
on d.a_id = ids.id
order by ids.id;
Trying to use a while look is troublesome. Such programming constructs would be in programming blocks. They are designed for stored procedures, functions, and triggers.
I assume that your code is actually over-simplified, because this probably does what you want:
select d.a_id, count(*)
timestampdiff(second, min(d.`Time`), min(d.`Time`))
from data d
where a.id between 1 and 10
group by d.a_id
Use like this
DECLARE #counter INT = 0
WHILE #counter < 10
BEGIN
select a_id, count(ID), TIMESTAMPDIFF(SECOND,MIN(`Time`),MAX(`Time`))
from data
where a_id = #counter
SET #counter = #counter + 1;
END

How to find Running Multiplication

Not sure is this the right title. I need to find the cumulative multiplication as like running total.
Searched the forum and got a excellent answer. But it is not the exact answer for me.
so modified the answer to my requirement.
SELECT *,
(SELECT CASE
WHEN Min(Abs(Column1)) = 0 THEN 0
ELSE Exp(Sum(Log(Abs(NULLIF(Column1, 0))))) -- the base mathematics
* Round(0.5 - Count(NULLIF(Sign(Sign(Column1) + 0.5), 1))%2, 0) -- pairs up negatives
END
FROM TEMP a
WHERE B.ID >= A.ID) as Running_Mul
FROM TEMP B
And I got my answer. Now Is there any better way of doing this in Sql Server 2008?
Sample data:
ID Column1
-- -------
1 1
2 2
3 4
4 8
5 -2
Expected Result:
ID Column1 Running_Mul
-- ------- -----------
1 1 1
2 2 2
3 4 8
4 8 64
5 -2 -128
Sql Fiddle
Your method is pretty reasonable. Good catch on the nullif() in the sum(), by the way. Although the else clause is computed only after the then, components of the else are calculated during the aggregation -- so log(0) would return an error.
I think there are some simpler ways to calculate the sign, such as:
power(-1, sum(case when column1 < 0 then 1 else 0 end))
or:
(case when sum(case when column1 < 0 then 1 else 0 end) % 2 = 0 then 1 else -1 end)
However, which version is "simpler" is a matter of opinion.
Here is another approach which I use in my SPs :
USE DB
GO
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
IF(OBJECT_ID('TEMP') IS NOT NULL)
DROP TABLE TEMP
CREATE TABLE TEMP (ID INT, Column1 INT)
INSERT INTO TEMP VALUES
(1,1),
(2,2),
(3,4),
(4,8),
(5,-2)
DECLARE #result TABLE(ID INT, Column1 INT, calc INT)
DECLARE #Calc INT = 1
INSERT INTO #result (ID,Column1)
SELECT ID,Column1 FROM TEMP ORDER BY ID
UPDATE #result SET #Calc = calc = Column1 * #Calc
SELECT * FROM #result
I found a blog in which different methods to solve such problem, have been compared. check here.

SQL omit columns with all same value

Is there a way to write a SQL query to omit columns that have all of the same values? For example,
row A B
1 9 0
2 7 0
3 5 0
4 2 0
I'd like to return just
row A
1 9
2 7
3 5
4 2
Although it is possible to use SQL to find if all rows in a column have identical values, there is no way to make a fixed SQL statement not return a column based on the content of the query.
Here is how to find out if all items in a column have identical values:
SELECT COUNT(row)=COUNT(DISTINCT B) FROM my_table
You can run a preliminary query to see if a column needs to be displayed, and then form the query dynamically, including the column only when you need it.
The only way to change what columns are returned is by executing separate queries. So you'd have to do something like:
IF EXISTS(SELECT null FROM myTable WHERE B <> 0)
BEGIN
SELECT row, A, B FROM myTable
END
ELSE
BEGIN
SELECT row, A FROM myTable
END
However it's generally bad practice to return different columns based on the data - otherwise you make the client determine if a particular column is in the result set first before trying to access the data.
This sort of requirement is more commonly done when displaying the data, e.g. in a web page, in a report, etc.
There is no way to write a query statement that will only return columns that have disparate values. You can however use some conditonnal statements to execute different queries based on your needs.
You could also insert your query result into a temporary table, loop over the columns, build a new select statement that includes only the columns that have different values and finally execute the statement.
Note: You should probably just include bit columns to indicate wheter columns are all duplicates or not. The application could then just discard any column that has been indicated as all duplicates.
Anyway, here's an example solution for SQL SERVER
-- insert results into a temp table
SELECT *
INTO #data
FROM (
SELECT 1 AS col1, 1 AS col2, 2 AS col3
UNION ALL
SELECT 2, 1, 3
) d;
DECLARE
#column sysname,
#sql nvarchar(max) = '',
#finalSql nvarchar(500) = 'SELECT ',
#allDuplicates bit;
DECLARE colsCur CURSOR
FOR
SELECT name
FROM tempdb.sys.columns
WHERE object_id = OBJECT_ID('tempdb..#data');
OPEN colsCur;
FETCH NEXT FROM colsCur INTO #column;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = N'SELECT #allDuplicates = CASE COUNT(DISTINCT ' + #column + ') WHEN 1 THEN 1 ELSE 0 END FROM #data';
EXEC sp_executesql #sql, N'#allDuplicates int OUT', #allDuplicates = #allDuplicates OUT;
IF #allDuplicates = 0 SET #finalSql = #finalSql + #column + ',';
FETCH NEXT FROM colsCur INTO #column;
END
CLOSE colsCur;
DEALLOCATE colsCur;
SET #finalSql = LEFT(#finalSql, LEN(#finalSql) - 1) + ' FROM #data';
EXEC sp_executesql #finalSql;
DROP TABLE #data;

SQL SELECT statement, column names as values from another table

I'm working on a database which has the following table:
id location
1 Singapore
2 Vancouver
3 Egypt
4 Tibet
5 Crete
6 Monaco
My question is, how can I produce a query from this which would result in column names like the following without writing them into the query:
Query result:
Singapore , Vancouver, Egypt, Tibet, ...
< values >
how can I produce a query which would result in column names like the
following without writing them into the query:
Even with crosstab() (from the tablefunc extension), you have to spell out the column names.
Except, if you create a dedicated C function for your query. The tablefunc extension provides a framework for this, output columns (the list of countries) have to be stable, though. I wrote up a "tutorial" for a similar case a few days ago:
PostgreSQL row to columns
The alternative is to use CASE statements like this:
SELECT sum(CASE WHEN t.id = 1 THEN o.ct END) AS "Singapore"
, sum(CASE WHEN t.id = 2 THEN o.ct END) AS "Vancouver"
, sum(CASE WHEN t.id = 3 THEN o.ct END) AS "Egypt"
-- more?
FROM tbl t
JOIN (
SELECT id, count(*) AS ct
FROM other_tbl
GROUP BY id
) o USING (id);
ELSE NULL is optional in a CASE expression. The manual:
If the ELSE clause is omitted and no condition is true, the result is null.
Basics for both techniques:
PostgreSQL Crosstab Query
You could do this with some really messing dynamic sql but I wouldn't recommend it.
However you could produce something like below, let me know if that stucture is acceptable and I will post some sql.
Location | Count
---------+------
Singapore| 1
Vancouver| 0
Egypt | 2
Tibet | 1
Crete | 3
Monaco | 0
Script for SelectTopNRows command from SSMS
drop table #yourtable;
create table #yourtable(id int, location varchar(25));
insert into #yourtable values
('1','Singapore'),
('2','Vancouver'),
('3','Egypt'),
('4','Tibet'),
('5','Crete'),
('6','Monaco');
drop table #temp;
create table #temp( col1 int );
Declare #Script as Varchar(8000);
Declare #Script_prepare as Varchar(8000);
Set #Script_prepare = 'Alter table #temp Add [?] varchar(100);'
Set #Script = ''
Select
#Script = #Script + Replace(#Script_prepare, '?', [location])
From
#yourtable
Where
[id] is not null
Exec (#Script);
ALTER TABLE #temp DROP COLUMN col1 ;
select * from #temp;

Use comparison signs inside a sql case statement

I'm looking for a way to build case statements in a sql select query using less than and greater than signs. For example, I want to select a ranking based on a variable:
DECLARE #a INT
SET #a = 0
SELECT CASE
WHEN #a < 3 THEN 0
WHEN #a = 3 THEN 1
WHEN #a > 3 THEN 2
END
I'd like to write it as:
DECLARE #a INT
SET #a = 0
SELECT CASE #a
WHEN < 3 THEN 0
WHEN 3 THEN 1
WHEN > 3 THEN 2
END
...but SQL doesn't let me use the < and > signs in this way. Is there a way that I can do this is SQL 2005, or do I need to use the code like in the first one.
The reason for only wanting the code there once is because it would make the code a lot more readable/maintainable and also because I'm not sure if SQL server will have to run the calculation for each CASE statement.
I'm looking for a VB.NET case statement equivelent:
Select Case i
Case Is < 100
p = 1
Case Is >= 100
p = 2
End Select
Maybe it's not possible in SQL and that's ok, I just want to confirm that.
You can use the SIGN function as
DECLARE #a INT
SET #a = 0
SELECT CASE SIGN(#a - 3)
WHEN -1 THEN 0
WHEN 0 THEN 1
WHEN 1 THEN 2
END
If #a is smaller than 3, then #a - 3 results in a negative int, in which SIGN returns -1.
If #a is 3 or greater, then SIGN returns 0 or 1, respectively.
If the output you want is 0, 1 and 2, then you can simplify even more:
DECLARE #a INT
SET #a = 0
SELECT SIGN(#a - 3) + 1
Using SIGN as suggested by #Jose Rui Santos seems a nice workaround. An alternative could be to assign the expression an alias, use a subselect and test the expression (using its alias) in the outer select:
SELECT
…,
CASE
WHEN expr < 3 THEN …
WHEN expr > 3 THEN …
END AS …
FROM (
SELECT
…,
a complex expression AS expr
FROM …
…
)
SELECT
CASE
WHEN ColumnName >=1 and ColumnName <=1 THEN 'Fail'
WHEN ColumnName >=6 THEN 'Pass'
ELSE 'Test'
END
FROM TableName