Select specific columns by their alias, and later order by it - sql

My table are the following
+----+----------+--------+
| id | priority | User |
+----+----------+--------+
| 1 | 2 | [null] |
| 2 | 1 | [null] |
| 3 | 3 | Tony |
| 4 | 2 | John |
| 5 | 2 | Andy |
| 6 | 1 | Mike |
+----+----------+--------+
My goal is to extract them, and order by the following combined conditions:
priority = 1
User is null
+----+----------+--------+-----------+
| id | priority | User | peak_rows |
+----+----------+--------+-----------+
| 1 | 2 | [null] | 1 |
| 2 | 1 | [null] | 1 |
| 6 | 1 | Mike | 0 |
| 3 | 3 | Tony | 1 |
| 4 | 2 | John | 0 |
| 5 | 2 | Andy | 0 |
+----+----------+--------+-----------+
This is what I guess I can do
select
id,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User is NULL THEN 1 ELSE 0 END as c2,
c1 + c2 AS peak_rows
FROM mytable
ORDER BY peak_rows DESC
but it cause an error:
ERROR: column "c1" does not exist
LINE 5: c1+ c2as pp
^
SQL state: 42703
Character: 129
I don't know why I make 2 columns(c1 and c2), but I can not use it later.
Any good idea to do that?

You are not making two columns and using them later, you are making them and want to use them at the same time. You could use a subquery.
SELECT a.id, a.priority, a.User, a.c1 + a.c2 AS peak_rows
FROM
(SELECT id,
priority,
User,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User IS NULL THEN 1 ELSE 0 END as c2,
FROM mytable) a
ORDER BY peak_rows DESC;

select
id,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User is NULL THEN 1 ELSE 0 END as c2,
(CASE WHEN priority = 1 THEN 1 ELSE 0) + ( CASE WHEN User is NULL THEN 1 ELSE 0 END) AS peak_rows
FROM mytable
ORDER BY peak_rows DESC

I suppose your aim is to order by those c1 and c2, so you can directly use in the order by clause. You just need to interchange 0 and 1 in the case..when statements. And depending on your priority=1 criteria id=2 must stay at the top.
with mytable( id, priority, "User" ) as
(
select 1 , 2, null union all
select 2, 1, null union all
select 3, 3, 'Tony' union all
select 4, 2, 'John' union all
select 5, 2, 'Andy' union all
select 6, 1, 'Mike'
)
select *
from mytable
order by ( case when priority = 1 then 0 else 1 end ) +
( case when "User" is null then 0 else 1 end );
id priority User
-- -------- -------
2 1 [null]
1 2 [null]
6 1 Mike
3 3 Tony
4 2 John
5 2 Andy
Demo

Related

Efficient ROW_NUMBER increment when column matches value

I'm trying to find an efficient way to derive the column Expected below from only Id and State. What I want is for the number Expected to increase each time State is 0 (ordered by Id).
+----+-------+----------+
| Id | State | Expected |
+----+-------+----------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 4 | 2 |
| 6 | 2 | 2 |
| 7 | 3 | 2 |
| 8 | 0 | 3 |
| 9 | 5 | 3 |
| 10 | 3 | 3 |
| 11 | 1 | 3 |
+----+-------+----------+
I have managed to accomplish this with the following SQL, but the execution time is very poor when the data set is large:
WITH Groups AS
(
SELECT Id, ROW_NUMBER() OVER (ORDER BY Id) AS GroupId FROM tblState WHERE State=0
)
SELECT S.Id, S.[State], S.Expected, G.GroupId FROM tblState S
OUTER APPLY (SELECT TOP 1 GroupId FROM Groups WHERE Groups.Id <= S.Id ORDER BY Id DESC) G
Is there a simpler and more efficient way to produce this result? (In SQL Server 2012 or later)
Just use a cumulative sum:
select s.*,
sum(case when state = 0 then 1 else 0 end) over (order by id) as expected
from tblState s;
Other method uses subquery :
select *,
(select count(*)
from table t1
where t1.id < t.id and state = 0
) as expected
from table t;

SQL Grouping entries with a different value

Let's assume I have a report that displays an ID and VALUE from different tables
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
4 | 2 | 0 |
5 | 2 | 0 |
My goal is to display this table with grouped IDs and VALUEs. My rule to grouping VALUEs would be "If VALUE contains atleast one '1' then display '1' otherwise display '0'".
My current SQL is (simplified)
SELECT
TABLE_A.ID,
CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END AS VALUE
FROM TABLE_A, TABLE_B, TABLE_C
GROUP BY
TABLE_A.ID
(CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END)
The output is following
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 2 | 0 |
Which is half way to the output I want
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 2 | 0 |
So my Question is: How do I extend my current SQL (or change it completely) to get my desired output?
If you are having only 0 and 1 as distinct values in FOREIGN_VALUE column then using max() function as mentioned by HoneyBadger in the comment will fulfill your requirement.
SELECT
ID,
MAX(FOREIGN_VALUE) AS VALUE
FROM (SELECT
ID,
CASE WHEN FOREIGN_VALUE = 1
THEN 1
ELSE 0
END AS FOREIGN_VALUE
FROM TABLE,
FOREIGN_TABLE)
GROUP BY
ID;
Assuming value is always 0 or 1, you can do:
select id, max(value) as value
from t
group by id;
If value can take on other values:
select id,
max(case when value = 1 then 1 else 0 end) as value
from t
group by id;

Select all rows, with specific characteristics, with same subdata by grouping them through a same row value in oracle

I have a table with the next structure:
Data structure:
| CONTRACT | CONNECTION | STATE |
| 1 | AAA | Y |
| 2 | AAA | Y |
| 3 | BBB | N |
| 4 | BBB | N |
| 5 | BBB | N |
| 6 | BBB | N |
| 7 | AAA | Y |
| 8 | CCC | Y |
| 9 | CCC | N |
| 10 | AAA | Y |
| 11 | CCC | N |
I'd like to select all rows/data that, considering groups defined by CONNECTION column as groups AAA, BBB and CCC, the query have to select only groups that have all rows of the STATE column with the value 'N', but always considering all data of the group.
So the result on the table would be:
Result needed:
| CONTRACT | CONNECTION | STATE |
| 3 | BBB | N |
| 4 | BBB | N |
| 5 | BBB | N |
| 6 | BBB | N |
I've looking for functions like decode, exists and so on but just don't see how solve this problem.
Any idea of how could I write a query for this purpose?
Try this:
SELECT CONTRACT, CONNECTION, STATE
FROM (
SELECT CONTRACT, CONNECTION, STATE,
COUNT(CASE WHEN STATE <> 'N' THEN 1 END) OVER (PARTITION BY CONNECTION) AS cnt
FROM mytable) t
WHERE t.cnt = 0
You can use SUM and HAVING:
ONLINE DEMO
SELECT *
FROM tbl t
WHERE
CONNECTION IN(
SELECT CONNECTION
FROM tbl
GROUP BY CONNECTION
HAVING
SUM(CASE WHEN STATE = 'N' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN STATE <> 'N' THEN 1 ELSE 0 END) = 0
)
The first condition in the HAVING clause makes sure that the CONNECTION has at least one STATE = 'N'. The second one makes sure that the CONNECTION has no other STATEs than 'N'.
You could use windowed COUNT:
WITH cte AS (
SELECT *
,COUNT(CASE WHEN STATE = 'N' THEN 1 END) OVER(PARTITION BY CONNECTION) AS num_of_n
,COUNT(CASE WHEN STATE <> 'N' OR STATE IS NULL THEN 1 END)
OVER(PARTITION BY CONNECTION) AS num_of_non_n
FROM mytable
)
SELECT CONTRACT, CONNECTION, STATE
FROM cte
WHERE num_of_n > 0 AND num_of_non_n = 0;
LiveDemo
Or correlated subquery:
SELECT *
FROM mytable m1
WHERE STATE = 'N'
AND NOT EXISTS (SELECT 1
FROM mytable m2
WHERE m1.CONNECTION = m2.CONNECTION
AND (STATE <> 'N' OR STATE IS NULL));
LiveDemo 2

Return all rows from a table and indicate with a new column whether they exist or not in another table

If I have 2 tables:
TABLE_SEARCHFIELDS:
FieldID | FieldName
--------------------
1 | MyField1
2 | MyField2
3 | MyField3
4 | MyField4
5 | MyField5
and
TABLE_CUSTOMSEARCHFIELDS:
UserID | FieldID
--------------------
1 | 1
1 | 2
1 | 5
2 | 2
2 | 4
2 | 5
and I would like to return all of the Searchfields from the first table, but would also like indicated whether that Searchfield is active for a particular user.
E.g. I want to query UserID = 1 and get the result:
FieldID | FieldName | Active
------------------------------
1 | MyField1 | 1
2 | MyField2 | 1
3 | MyField3 | 0
4 | MyField4 | 0
5 | MyField5 | 1
What is the best way to achieve this?
I would do this using exists:
select sf.*,
(case when exists (select 1
from customsearchfields csf
where csf.userid = 1 and csf.fieldid = sf.fieldid
)
then 1 else 0
end) as Active
from searchfields sf;
Assuming you have no duplicate rows, you can also do this using a left join:
select sf.*, (case when csf.userid is not null then 1 else 0 end) as Active
from searchfields sf left join
customsearchfields csf
on csf.userid = 1 and csf.fieldid = sf.fieldid;

Increment variable in sql query

Using SQL Server 2008, I want to query a table like so:
| ID | Number
-------------
| 1 | 0
| 2 | 0
| 3 | 1
| 4 | 0
| 5 | 0
| 6 | 1
| 7 | 1
| 8 | 1
The result should be the same table with an additional column that counts.
The method of counting is: if the number in "number" equals to 1 - increment the counter by one for the next line.
An example of result for the provided table:
| ID | Number | Counter
-----------------------
| 1 | 0 | 1
| 2 | 0 | 1
| 3 | 1 | 1
| 4 | 0 | 2
| 5 | 0 | 2
| 6 | 1 | 2
| 7 | 1 | 3
| 8 | 1 | 4
How can this be achieved?
select [ID], [Number],
isnull(1+(select sum([Number]) from Table1 t2 where t2.ID<t1.Id),1)
from Table1 t1
SQL Fiddle to test
This is not too hard to do. What you are looking for is very much like the running total, which you get with sum and a windowing clause.
select id, num, 1 + sum(num) over (order by id) - num as counter
from mytable
order by id;
Here is an SQL fiddle: http://sqlfiddle.com/#!4/958e2a/1.
You can use recursive select too but it is a bit complicated but if you insert other numbers which are greater than 1 it work fine:
with tab(id,number,counter,rn) as
(select t.*,1 as counter,1 as rn from table1 t where id = 1
union all
select t.*,case when t.number = 1 then counter + 1 else counter end as counter,
rn + 1 as rn from table1 t,tab where t.id = tab.rn + 1),
tab2 as (select id,number,counter from tab)
select id,number,case when number = 1 then counter - 1
else counter end as counter from tab2;
SQL Fiddle