Query not returning records with a boolean column set to FALSE? - sql

This one is driving me mad - I must be missing something very simple here but have been Googling for 2 hours and have got nowhere. I'm building a Rails app with Postgresql 9.5.1 that handles appointments for barbers. One of the columns is available - if the appointment is available to book or not - it's a boolean. I need to get the next 5 records for a given appointment ID.
select *
from appointments
where id > 13
and barber_id = 1
limit 5
This returns 5 records, but the available field is all true, even though records 14-18 exist with attributes of available = FALSE.
If I use the query
select * from appointments where id > 13 and barber_id = 1 and (available = false) limit 5
Then it returns only records with available = FALSE, but I want all records that match, whether TRUE or FALSE.
I have also tried
select * from appointments where id > 13 and barber_id = 1 and (available = false or available = true) limit 5
But this also only returns available = TRUE records.
Tearing my hair out here - what am I missing?! A default or something?

If your id's are not in sorted order, you might want to sort them and return
select * from appointments where id > 13 and barber_id = 1 and
(available = false or available = true) order by id asc limit 5
And your boolean thing can be completely removable if it is a not null column, since you are checking for both true and false :)

Related

SQL query to pull certain rows based on values in other rows in the same table

I have a set of data that contains 2 sets of identifiers: a unique number for that record, Widget_Number, and the original unique number for the record, Original_Widget_Number. Typically these two values are identical but when a record has been revised, the a new record is created with a new Widget_Number, preserving the old Widget_Number value in Original_Widget_Number. IE SELECT * FROM widgets WHERE Widget_Number != Original_Widget_Number returns all records that have been changed. (Widget_Number increments by 10 for new widgets and by 1 for revised widgets.)
I would like to return all records that were changed as well as the original records related to those records. For example if I had a table containing:
Widget_Number Original_Widget Number More_Data
1: 10 10 Stephen
2: 11 10 Steven
3: 20 20 Joe
I would like a query to return rows 1 & 2. I know I could loop trough this in a higher-level language but is there a straightforward way to do this in MS SQL?
using exists():
select *
from widgets as t
where exists (
select 1
from widgets as i
where i.original_widget_number = t.original_widget_number
and i.widget_number != i.original_widget_number
)
or in()
select *
from widgets as t
where t.original_widget_number in (
select i.original_widget_number
from widgets as i
where i.widget_number != i.original_widget_number
)
The following should get both the records that have changed and the original records:
select w.*
from widgets w
where w.widget_number <> w.original_widget_number or
exists (select 1
from widgets w2
where w.widget_number = w2.original_widget_number and
w2.widget_number <> w2.original_widget_number
);
select * from widget
where original_widget_number in
(select original_widget_number from widget
where widget_number <> original_widget_number)

Sql or operator

I have something I don't understand.
If my table is going like this:
name age
---- ----
din 18
mari 35
And i did this query:
select * from usernames
where age < 20 or name not like 'din'
And all the table and rows printed, my goal is to print who was not called 'din' and under the age of 20.
Can someone tell me where is my mistake?
i asked here another q .. sorry about it.
Explanation:
where age < 20 or name not like 'din'
breaks down to:
where <something> or <something else>
For each row, < something > and < something else > will be evaluated. The or means the result will be true if either is true. Only one of them needs to be true for the row to be selected.
In your example, the first row meets the age < 20 condition. The rest of the test is not important (and it evaluates as false) because we already have a true. The second row evaluates as false for age < 20 but true for name not like 'din' so as explained above, if either side of the OR is true, the result is true, and thus row 2 is returned in the results.
That is why both rows are returned.
Add this data to your table to help illustrate:
name age
---- ----
din 28 (both sides false)
mari 15 (both sides true)

unusual sql server query result

Let's say I have a table called nameAge:
ID Name Age
1 X 12
2 Y 12
3 null null
4 Z 12
and when I run a query like:
select * from nameAge where Age <> 12
it returns me an empty result set while I have row with id 3 where age is different than null?
Using Sql Server 2008 R2.
Any ideas?
Edit: Possibility to be duplicate with suggested answer may be at one point but does not cover at all and it shows how to use null values when compared with null but what I wanted to ask was about the result set which includes null values
This is the intended behavior. You cannot compare NULL values using = or <>. You have to use IS NULL or IS NOT NULL.
If you want NULL values only use IS NULL:
select * from nameAge where age IS NULL
If you want NULL values with age <> 12 values, use:
select * from nameAge where age <> 12 OR age IS NULL
The expression
WHERE NULL <> 12
does not return TRUE or FALSE, but actually returns UNKNOWN. This means that the third record in your table will not be returned by your query.
As #ughai mentioned, you should use IS NULL instead to query that record:
SELECT * FROM nameAge WHERE age IS NULL
Have a look at the Microsoft SQL Server documentation for more information.
When you are dealing with NULLs you should be always careful because of 3 valued logic used in Sql Server(when a predicate can be evaluated to TRUE, FALSE or UNKNOWN). Now here is a classic select statement where many newcomers make a mistake, suggesting that the statement would return all rows where Age <> 12 including NULLs.
But if you know the easy fact that comparing NULL to any value, even to NULL itself will evaluate to UNKNOWN it is getting more clear what is going on. WHERE clause will return ONLY those rows where predicate is evaluated to TRUE. Rows where predicate evaluates to FALSE or UNKNOWN will be filtered out from resultset.
Now let's see what is going on behind the scene. You have 4 rows:
ID Name Age
1 X 12
2 Y 12
3 null null
4 Z 12
and the predicate is:
where Age <> 12
When you evaluate this predicate for each row you get:
ID Name Age Evaluation result
1 X 12 FALSE --(because 12 <> 12 is FALSE)
2 Y 12 FALSE --(because 12 <> 12 is FALSE)
3 null null UNKNOWN --(because NULL <> 12 is UNKNOWN)
4 Z 12 FALSE --(because 12 <> 12 is FALSE)
Now remember that WHERE clause will return only rows where predicate evaluates to TRUE and it is clear that you will not get any result because no row evaluates to TRUE.

Calculated Column to Determine if Value Appears in Column of Any Row Filtered on Non-Unique ID

I have a set of data in PowerPivot that has non-unique IDs on which I group items for reporting purposes. Each row is a unique item called a task and multiple tasks may be associated with one item called a review. Each task may require action. As such, the table looks something like this (without the ReviewAction column):
TaskID Action ReviewID ReviewAction
------------------------------------------------
1 True 1 True
2 False 1 True
3 False 2 False
4 True 3 True
5 False 4 False
6 False 4 False
7 False 5 True
8 True 5 True
9 False 5 True
Is there a way to produce ReviewAction as a calculated column (Display True if any tasks associated with a review require action)? For example, Review 1 contains Tasks 1 and 2. Task 1 requires action so ReviewAction is set to True for any row associated with Review 1. Likewise, Review 5 contains Tasks 7, 8, and 9. Only Task 8 requires action, but I want ReviewAction to display True for all rows associated with Review 5.
I have used the following function to count if Review IDs are duplicates, and if so, how many duplicates there are:
=CALCULATE(COUNTROWS('TableName'), FILTER('TableName', [ReviewID]=EARLIER([ReviewID])))
I haven't been able to figure out a way to use this same filtering technique to produce the ReviewAction column, however.
The reason I'm trying to produce this column is so that I can create a chart that counts the Review items (just with a distinct count) and includes a slicer to filter by reviews that require action or not. In order to create a slicer, I need that "ReviewAction" value to exist as a column.
H Jimmy,
Very interesting case, I was dealing with something similar and found an elegant (not sure how performance-smart) way to do this using X-functions. In this case, MAXX will help:
=
MAXX (
FILTER ( 'Table', [Review ID] = EARLIER ( [Review ID] ) ),
INT ( [Action] )
)
It takes sub-tables grouped by Review ID and then calculates maximum for this group, using INTEGER value of True/False statements. Then, just wrap it inside an IF clause:
=
IF (
MAXX (
FILTER ( 'Table', [Review ID] = EARLIER ( [Review ID] ) ),
INT ( [Action] )
)
= 1,
TRUE (),
FALSE ()
)
And you will get what you need. The source Excel 2013 file can be downloaded here.
Hope this helps.

Return true if all column values are true

Is there a faster way in PostgreSQL to essentially do an if on several rows?
Say I have a table
ticket | row | archived
1 | 1 | true
1 | 2 | true
1 | 3 | true
2 | 1 | false
2 | 2 | true
Is there any way I could do an if statement across down the column where ticket = ?
So that where ticket = 1 would be true because
true && true && true = true
and where ticket = 2 would be false because
false && true = false
Or should I just stick with
SELECT ( (SELECT COUNT(*) FROM table WHERE ticket = 1)
= (SELECT COUNT(*) FROM table WHERE ticket = 1 AND archived = true) )
Aggregate function bool_and()
Simple, short, clear:
SELECT bool_and(archived)
FROM tbl
WHERE ticket = 1;
The manual:
true if all input values are true, otherwise false
Subquery expression EXISTS
Assuming archived is defined NOT NULL. Faster, but you have to additionally check whether any rows with ticket = 1 exist at all, or you'll get incorrect results for non-existing tickets:
SELECT EXISTS (SELECT FROM tbl WHERE ticket=1)
AND NOT
EXISTS (SELECT FROM tbl WHERE ticket=1 AND NOT archived);
Indices
Both forms can use an index like:
CREATE INDEX tbl_ticket_idx ON tbl (ticket);
.. which makes both fast, but the EXISTS query faster, because this form can stop to scan as soon as the first matching row is found. Hardly matters for only few rows per ticket, but matters for many.
To make use of index-only scans you need a multi-column index of the form:
CREATE INDEX tbl_ticket_archived_idx ON tbl (ticket, archived);
This one is better in most cases and any version of PostgreSQL. Due to data alignment, adding a boolean to the integer in the index will not make the index grow at all. Added benefit for hardly any cost.
Update: this changes in Postgres 13 with index deduplication. See:
Is a composite index also good for queries on the first field?
However, indexed columns prevent HOT (Heap Only Tuple) updates. Say, an UPDATE changes only the column archived. If the column isn't used by any index (in any way), the row can be HOT updated. Else, this shortcut cannot be taken. More on HOT updates:
Redundant data in update statements
It all depends on your actual workload.
How about something like:
select not exists (select 1 from table where ticket=1 and not archived)
I think this might be advantageous over comparing the counts, as a count may or may not use an index and really all you need to know is if any FALSE rows exist for that ticket. I think just creating a partial index on ticket could be incredibly fast.
SQL Fiddle
select not false = any (
select archived
from foo
where ticket = 1
)
SQL Fiddle