Case statements on where clause? - sql

I am a bit new to case statements but below is my query.
Table1
col1 col2 col3 col4 Month Freeze
1 13 25 37 1 0
3 15 27 39 2 1
4 16 28 40 2 0
5 17 29 41 3 1
6 18 30 42 3 0
7 19 31 43 4 1
8 20 32 44 4 0
9 21 33 45 5 1
10 22 34 46 5 0
11 23 35 47 6 0
'Results i want like':
Select all records from Month1 to Month 12 where 'Freeze = 1' ,
if any month between 1 to 12 does not have Freeze = 1 then give me records for Freeze= 0 'just for the months which does not have 'Freeze = 1'
My best non-working attempt:
select * from tb1 where Month between 2 and 6 and freeze = 1 or Month in ('1' ,'2') and freeze = 0

Here is one option using an outer join:
select t1.*
from Table1 t1
left join Table1 t2 on
t1.month = t2.month and t2.freeze = 1
where t1.freeze = 1
and t1.month between 1 and 12
or t2.month is null
SQL Fiddle Demo
Results:
COL1 COL2 COL3 COL4 MONTH FREEZE
1 13 25 37 1 0
3 15 27 39 2 1
5 17 29 41 3 1
7 19 31 43 4 1
9 21 33 45 5 1
11 23 35 47 6 0
Note, depending on your data, you may need to use distinct with this method.

The following is a method that uses standard SQL:
Select t1.*
from table1 t1
where Freeze = 1 and month between 1 and 12 or
(not exists (select 1 from table where Freeze = 1 and month between 1 and 12) and
month = 0
);
Using window functions, you could also do:
select t1.*
from (select t1.*,
count(case when freeze = 1 and month between 1 and 2) over () as freeze1cnt
from table1 t1
) t1
where month between 1 and 2 and freeze = 1 or
month = 0 and freeze1cnt = 0;

Related

Update rows based on range around values without changing rows that are not initially within range

In a local SQLite (vs 3.29.0) database, there is a table with 3 columns (excluding the rowID). Two contain values, and one contains categories. I want to update the category based on a range around the values of one specific category. It needs to be possible that the category that is SET is the same category as the one that determines the range.
Example:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
1
7
26
26
1
8
42
42
1
9
49
49
1
10
52
52
1
11
71
71
1
12
90
90
1
13
17
17
1
I want rows to be changed to category 2, based on a range of 4 around value and a range of 2 around value2. This should change only rows 6, 9 and 11:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
2
7
26
26
1
8
42
42
2
9
49
49
1
10
52
52
1
11
71
71
2
12
90
90
1
13
17
17
1
My current SQL statement is:
UPDATE tablename
SET Category = 2
WHERE (Category != 2
AND EXISTS (
SELECT *
FROM tablename t
WHERE t.Category = 2
AND tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 +2)
);
of which the result is:
id
Value
Value2
Category
1
20
20
2
2
30
30
2
3
40
40
2
4
70
70
2
5
5
5
1
6
19
19
2
7
26
26
1
8
42
42
2
9
49
49
1
10
52
52
1
11
71
71
2
12
90
90
1
13
17
17
2
What appears to be happening is that due to row 6 changing to category 2, row 13 is now within range of the values of a row that is in category 2, and therefore is also assigned category 2. How do I change the statement so that the SET is only applied to the values that were within range initially?
See the demo for the example.
If your version of SQLite is 3.33.0+ you can use the join-like UPDATE...FROM syntax to perform a self join in the UPDATE statement:
UPDATE tablename AS t1
SET Category = t2.Category
FROM tablename AS t2
WHERE t2.Category = 2
AND t1.Category <> t2.Category
AND t1.Value BETWEEN t2.Value - 4 AND t2.Value + 4
AND t1.Value2 BETWEEN t2.Value2 - 2 AND t2.Value2 + 2;
For previous versions of SQLite, first create a temporary table with all the rows of the table with Category = 2:
CREATE TEMPORARY TABLE t AS
SELECT * FROM tablename WHERE Category = 2;
and then update the table:
UPDATE tablename
SET Category = 2
WHERE Category <> 2
AND EXISTS (
SELECT 1
FROM t
WHERE tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 + 2
);
See the demo.

In SQL, how to select minimum value of a column and group by other columns?

I have a lookup table below:
id ref order
1 6 0
2 6 0
3 7 0
5 34 0
6 33 0
6 255 1
9 12 0
9 80 1
12 7 0
12 76 1
13 10 0
15 12 0
16 6 0
16 7 1
17 6 1
17 63 0
18 7 0
19 7 1
19 75 0
20 6 0
20 63 1
So in the lookup table (tab_lkp), it has column [id] (the IDs of entities), [ref] (the reference id that points to other entities in another table) and [order] (tells the order of reference, smaller order means higher priority).
My expectation is that, for each of the IDs, only one ref with the smallest order is selected. My code is (by following Phil's answer):
select id
, ref
, min_order = min(order)
from [dbo].[tab_lkp]
group by id, ref
order by id, ref
But the code doesn't work for me, the results still contains multiple records for each of the IDs:
id ref order
1 6 0
2 6 0
3 7 0
5 34 0
6 33 0
6 255 1
9 12 0
9 80 1
12 7 0
12 76 1
13 10 0
15 12 0
16 6 0
16 7 1
17 6 1
17 63 0
18 7 0
19 7 1
19 75 0
20 6 0
20 63 1
Could you please let me know what is wrong with my code? And how should I achieve my goal?
From an ANSI sql approach:
select x2.id, x2.ref, x2.order
from MyTable x2
inner join
(
select id, min(order) as min_order
from MyTable
group by id
) x1
on x1.id = x2.id
and x1.min_order = x2.order
You would normally do this using row_number():
select t.*
from (select t.*, row_number() over (partition by id order by ref) as seqnum
from [dbo].[tab_lkp] t
) t
where seqnum = 1;
or by using a subquery that does exactly what you state that you want,
"for each of the IDs, only one ref with the smallest order is selected"
Select * from tab_lkp t
Where order =
(Select Min(order) from tab_lkp
where Id = t.Id)

SQL-How to get last 7 days data,display 0 when there is no record for specific day

22-03-2016 0 0 15 15
23-03-2016 1 2 12 15
24-03-2016 0 1 15 16
25-03-2016 0 1 9 10
26-03-2016 0 0 1 1
28-03-2016 0 0 13 13
29-03-2016 0 0 17 17
30-03-2016 0 1 19 20
31-03-2016 0 1 10 11
there is no data for 27,I want 0,0,0 if there is no data.
You need to create a calendar table and left join with your source table
select c,date,s.coalesce(s.col1,0) as col1,
coalesce(s.col2,0) as col2 ,coalesce(s.col3,0) as col3
from calendar as c
left join source_table as s on c.date=s.date

count of matching rows and select 30 from each count group in hive

How do I count the matching rows for the below sample data
ID Attribute 1 Attribute 2
1 A AA
2 B CC
3 C BB
4 A AA
5 C BB
6 D AA
7 B AA
8 C DD
9 A AB
10 A AA
the out put should look like this
ID Attribute 1 Attribute 2 count(Attribute1+Attribute2)
1 A AA 3
2 B CC 1
3 C BB 2
4 A AA 3
5 C BB 2
6 D AA 1
7 B AA 1
8 C DD 1
9 A AB 1
10 A AA 3
and then select 50% of rows from each count group. ex : for the mtaching row (A,AA) I need to select only 2 occurances. which would give me the the ID (1 and 4)
Here is the query:
select * from (select product_category_id,
count(1) over (partition by product_category_id) cnt,
row_number() over (partition by product_category_id) rn
from products) p where rn <= 0.5 * cnt;
Here is the sample result, product_category_id, cnt and then row number. In your case you need to have your two attributes in partition by clause.
59 24 1
59 24 2
59 24 3
59 24 4
59 24 5
59 24 6
59 24 7
59 24 8
59 24 9
59 24 10
59 24 11
59 24 12
You can do with SQL query like this.
SELECT *,
(SELECT COUNT(*)
FROM table AS t2
WHERE t1.[Attribute1] = t2.[Attibute1]
AND t1.[Attribute2] = t2.[Attibute2]) AS 'count(Attribute1+Attribute2)'
FROM table AS t1

Get result using two tables without using any loop

I have two tables Temp_Test and Temp_Marks.
Temp_Test is having following columns
id UserId QId QTitle QMarks
1 A1 1 A 5
2 A1 2 B 6
3 A1 3 C 4
4 A1 4 D 5
5 B3 1 A 8
6 B3 2 B 6
7 B3 3 C 4
8 B3 4 D 3
9 Z9 1 A 2
10 Z9 2 B 7
11 Z9 3 C 9
12 Z9 4 D 3
and Temp_Marks has following Columns
Id Score A B C D
1 1 10 5 40 12
2 2 20 10 50 23
3 3 30 15 60 34
4 4 40 20 70 54
5 5 50 25 80 84
6 6 60 30 90 36
7 7 70 35 10 85
8 8 80 40 20 97
9 9 90 45 30 58
10 10 100 50 100 48
I want to fetch result of particular UserId.
eg. If for A1, QTitle is A and marks is 5 in Temp_test table then fetch record from Column A where Score is 5 from Temp_Marks table.
without using while loop want to fetch all records of particular UserId or UserIds.
Can anyone help on this?
You may want to UNPIVOT the Temp_Marks first to easily join it with Temp_Test, and then PIVOT the result to achieve the desired format:
;WITH CteUnpivotedMarks AS(
SELECT
ID, Score, Col = 'A', Value = A
FROM Temp_Marks
UNION ALL
SELECT
ID, Score, Col = 'B', Value = B
FROM Temp_Marks
UNION ALL
SELECT
ID, Score, Col = 'C', Value = C
FROM Temp_Marks
UNION ALL
SELECT
ID, Score, Col = 'D', Value = D
FROM Temp_Marks
)
SELECT
UserId = t.UserID,
A = MAX(CASE WHEN t.QTitle = 'A' THEN c.Value END),
B = MAX(CASE WHEN t.QTitle = 'B' THEN c.Value END),
C = MAX(CASE WHEN t.QTitle = 'C' THEN c.Value END),
D = MAX(CASE WHEN t.QTitle = 'D' THEN c.Value END)
FROM Temp_Test t
INNER JOIN CteUnpivotedMarks c
ON t.QTitle = c.Col
AND t.QMarks = c.Score
GROUP BY t.UserID
RESULT
UserId A B C D
---------- ----------- ----------- ----------- -----------
A1 50 30 70 84
B3 80 30 70 34
Z9 20 35 30 34