If null, return multiple values - sql

I had posted a similar question earlier - a slightly different requirement here.
I have a textBox which returns the user-selected 'Number' value.(eg. : 100,200,300)
What needs to be done is basically check a table MyTable if a record/records exist for the particular Number value selected by user. If it returns NULL, then I need to return the records for the default Number value of 999.
MyTable:
id Number MyVal
1 100 55
2 200 66
3 400 22
4 400 12
5 999 23
6 999 24
Here's what I have so far :(Assuming textBoxInput(Number) = 300)
SELECT Myval
from MyTable
where id in (
SELECT ISNULL(
SELECT id
from MyTable
where Number=300,
select id
from MyTable
where Number = 999
)
)
So here, since Number=300 does not exist in the table, return the records for Number=999.
But when I run this query, I'm getting an error 'Subquery returned more than 1 value...'
Any suggestions/ideas?

This should work:
SELECT Myval
from MyTable
where Number = #Number
OR (NOT EXISTS(SELECT * FROM MyTable WHERE Number = #Number) AND Number = 999)

SELECT id, Myval
FROM MyTable
WHERE id = #id
UNION
SELECT id, 999 AS Myval
FROM MyTable
WHERE id = 999
AND #id IS NULL;

Related

Fill nulls with lag, but ignore nulls isn't supported

I have a table which looks like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
null
3
1
3000
4
1
2000
5
2
1000
1
2
null
2
2
100
3
I want that rows, without a value in money_earned (which means that the money_earned column was empty that day) - will and fill the money_earned with last known value, so it to look like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
2000
3
1
3000
4
1
2000
5
2
1000
1
2
1000
2
2
100
3
I have tried to look up for something like that, but postgresql doesn't support ignore nulls in lag window function :(
thank you!
The LOG() function would be suitable for this purpose, but in your
case you need to get first not null previous element. What the LOG()
function can't handle.
I suggest creating your own function that would return the first non-null value of the previous element.
CREATE OR REPLACE FUNCTION log_not_null(row_num int, dafult text = '1001') RETURNS text LANGUAGE plpgsql AS $$
DECLARE
result text;
index int;
BEGIN
if row_num > 0 then
index := row_num - 1;
else
return dafult;
end if;
result := (select money_earned from my_table offset index limit 1);
if result is null then
return log_not_null(index);
end if;
return result;
END
$$;
select id, log_not_null((row_number() over ())::int) as money_earned, days_since_start from my_table;
Note:
If the previous value is null, then the function is called recursively until it reaches the top element, and if the topmost element is also null, then the function will return the value from the dafult variable.
Demo in sqldaddy.io
You can do this (warning, untested! 😎):
UPDATE yourtable t1
SET money_earned = t3.money_earned
FROM yourtable
LEFT JOIN (SELECT
ID,
MAX(days_since_start) m
FROM yourtable
WHERE ID=t1.ID AND days_since_start<t1.ID) t2 ON t2.ID=i1.ID
INNER JOIN yourtable t3 ON t3.ID=t1.ID and t3.days_since_start = t2.days_since_start
WHERE t1.money_earned is null

How to randomly update rows in SQL for entire table

I have a table where for a column id, I want to randomly assign it "a","b", or "c" randomly and equally. There's also a condition where name has to be "test".
UPDATE table
WHERE name="test"
SET id=
CASE WHEN rand() < 1/3 THEN "a"
CASE WHEN rand() < 2/3 THEN "b"
CASE WHEN rand() < 1 THEN "c"
I'm not sure how this would work though, because I'm not sure if rand() will run for every row?
I'm pretty new to SQL so this is a bit confusing, thanks!
This answers the original version of the question
Just use arithmetic:
UPDATE table
SET id = 1 + floor(rand * 3)
WHERE name = 'test'
If by randomly and equally you mean that you want to ensure the number of rows in the 3 groups are distributed as equally as possible, you have to use ntile() like so:
CREATE TABLE public.test
( pk serial primary key,
id varchar,
name character varying
)
insert into test (name)values(generate_series(1,150)::varchar);
--99 rows of test data:
update test set name = 'test' where pk < 100;
-- partition by random and divide into 3 equal parts:
SELECT t.*, ntile(3) over(order by random())AS n from test t where name = 'test' ;
-- use this select in an update query:
UPDATE test
SET id = case sel.n when 1 then 'a'
when 2 then 'b'
when 3 then 'c'
END
FROM
(SELECT t.*, ntile(3) over(order by random())AS n
FROM test t WHERE name = 'test'
)sel
WHERE sel.pk = test.pk;
-- test result
select count(*) from test where name = 'test' group by id;
-- 33
-- 33
-- 33

Determine if any values satisfy a condition

How do I write an effiecient query to determine if 1 or more values are 10+ in a column. I am aware that I can count the values but this will scan all the records. Here is what I have so far:
SELECT COUNT(*) FROM MyTable WHERE [state] = 12 AND age > 110
I want this query to stop when it find the first person over 110 not scan the entire table. Is this possible?
You can use a subquery to return 1 or no row using this query:
SELECT TOP 1 1 as row_exists
FROM MyTable
WHERE [state] = 12 AND age > 110;
You can use a subquery to return 1 or NULL using this as a subquery:
SELECT (SELECT TOP 1 1 FROM MyTable WHERE [state] = 12 AND age > 110
) as row_exists;
You can put this into T-SQL using:
IF (EXISTS (SELECT 1 FROM MyTable WHERE [state] = 12 AND age > 110))
BEGIN
. . .
END;
TOP is not needed in an EXISTS subquery.
So you wish to have the scalar Boolean result? The exists will quite once any row matches the condition
DECLARE #Result bit =
(SELECT CASE WHEN EXISTS(SELECT * FROM MyTable WHERE [state] = 12 AND age > 110) THEN 1 ELSE 0 END)
I am not sure wether the it is helpful for you, but you can try to test:
For example you can want to determine the result set row count is 100.
you can use top 100 base your statement.
if the orignal result lines is more than 100, then ##ROWCOUNT will be true.
SELECT TOP 100 FROM MyTable WHERE [state] = 12 AND age > 110
IF ##ROWCOUNT=100
PRINT 'True'
ELSE
PRINT 'Flase'

Select rows until condition met

I would like to write an Oracle query which returns a specific set of information. Using the table below, if given an id, it will return the id and value of B. Also, if B=T, it will return the next row as well. If that next row has a B=T, it will return that, and so on until a F is encountered.
So, given 3 it would just return one row: (3,F). Given 4 it would return 3 rows: ((4,T),(5,T),(6,F))
id B
1 F
2 F
3 F
4 T
5 T
6 F
7 T
8 F
Thank you in advance!
Use a sub-query to find out at what point you should stop, then return all row from your starting point to the calculated stop point.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= (SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4)
Note, this assumes that the last record is always an 'F'. You can deal with the last record being a 'T' using a COALESCE.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= COALESCE(
(SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4),
(SELECT MAX(id) FROM yourTable )
)

SQL retrieval from tables

I have a table something like
EMPLOYEE_ID DTL_ID COLUMN_A COLUMN_B
---------------------------
JOHN 0 1 1
JOHN 1 3 1
LINN 0 1 12
SMITH 0 9 1
SMITH 1 11 12
It means for each person there will be one or more records with different DTL_ID's value (0, 1, 2 .. etc).
Now I'd like to create a T-SQL statement to retrieve the records with EMPLOYEE_ID and DTL_ID.
If the specified DTL_ID is NOT found, the record with DTL_ID=0 will be returned.
I know that I can achieve this in various ways such as checking if a row exists via EXISTS or COUNT(*) first and then retrieve the row.
However, I'd like to know other possible ways because this retrieval statement is very common in my application and my table have hundred thousand of rows.
In the above approach, I've had to retrieve twice even if the record with the DTL_ID specified exists, and I want to avoid this.
Like this:
SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = ??
UNION
SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = 0
AND NOT EXISTS (SELECT *
FROM table
WHERE EMPLOYEE_ID = ?? AND DTL_ID = ??)
You will of course have to fill in the ?? with the proper number.
If DTL_ID is always 0 or positive:
SELECT TOP 1 * FROM table
where EmployeeID = #EmployeeID and DTL_ID in (#DTL_ID,0)
order by DTL_ID desc
If you're working across multiple employees in a single query, etc, then you might want to use ROW_NUMBER() if your version of SQL supports it.
Use ISNULL(DTL_ID, 0) in your final SELECT query
SELECT E1.EMPLOYEE_ID, ISNULL(E2.DTL_ID, 0), E1.COLUMN_A, E1.COLUMN_B EMPLIYEES AS E1
LEFT JOIN EMPLIYEES AS E2
ON E1.EMPLOYEE_ID = E2.EMPLOYEE_ID AND E2.DTL_ID = 42
You can use top and union, e.g.:
declare #t table(id int, value int, c char)
insert #t values (1,0,'a'), (1,1,'b'), (1,2,'c')
declare #id int = 1;
declare #value int = 2;
select top(1) *
from
(
select *
from #t t
where t.value = #value and t.id = #id
union all
select *
from #t t
where t.value = 0
)a
order by a.value desc
If #value = 2 than query returns 1 2 c. If #value = 3 than query returns 1 0 a.
SELECT MAX(DTL_ID) ...
WHERE DTL_ID IN (#DTL_ID, 0)