Wired sql query - sql

sqlite> select sid,max(amount) from(select * from Am where year=2014);
1,600
sqlite> select sid from (select sid,max(amount) from(select * from Am where year=2014));
3
Where 3 come from?! the only option is 1..
Whats going on?
Thanks.

select sid, max(amount) doesn't seem very well-defined. I think you're assuming it will give you the sid for the row that has the max amount, I don't think that is a valid assumption.
I would write this as
select sid from Am where year = 2014
and amount = (select max(amount) from Am where year = 2014)
which can return multiple rows if there's a tie for the maximum amount.

Now you know what indeterminate means,if you don`t aggregate you need to group by, other databases dont even allow this operation.
Suppose you have these values:
sid amount
1 600
1 500
3 400
3 200
select sid,max(amount) from t
Will return an indeterminate value from sid and 600 for MAX.Sid might be correct or might not,that`s what indeterminate means,because you don`t have any control on that column.A proper query would be
select sid,max(amount) from t where year = 2014 GROUP BY sid
Or
select max(amount) from t

Related

Oracle SQL how to find count less than avg

my code is like :
SELECT
number,
name,
count(*) as "the number of correct answer"
FROM
table1 NATURAL JOIN table2
WHERE
answer = 'T'
GROUP BY
number,
name
HAVING
count(*) < avg(count(*))
ORDER BY
count(*);
Here I want to find the group with count less than the average number of count for each group, but here I failed to use HAVING or WHERE, could anyone help me?
How can I only select the 1 name1 2 since avg of count is (2+6+7)/3 = 5 and only 2 is less than avg.
number name count
1 name1 2
2 name2 6
3 name3 7
I would advise you to never use natural joins. They obfuscate the query and make the query a maintenance nightmore.
You can use window functions:
SELECT t.*
FROM (SELECT number, name,
COUNT(*) as num_correct,
AVG(COUNT(*)) OVER () as avg_num_correct
FROM table1 JOIN
table2
USING (?). -- be explicit about the column name
WHERE answer = 'T'
GROUP BY number, name
) t
WHERE num_correct < avg_num_correct;
As with your version of the query, this filters out all groups that have no correct answers.
I would place your current query logic into a CTE, and then tag on the average count in the process:
WITH cte AS (
SELECT number, name, COUNT(*) AS cnt,
AVG(COUNT(*)) OVER () AS avg_cnt
FROM table1
NATURAL JOIN table2
WHERE answer = 'T'
GROUP BY number, name
)
SELECT number, name, cnt AS count
FROM cte
WHERE cnt < avg_cnt;
Here we are using the AVG() function as an analytic function, with the window being the entire aggregated table. This means it will find the average of the counts per group, across all groups (after aggregation). Window functions (almost) always evaluate last.

Using COUNT and Return Percentage Against SUM of Records

Pretty simple question I suppose, but I can't find the answer on here!
I am trying to calculate the volume of records based on a WHERE clause, as well as return a percentage of those records based on the same where clause. The percentage would be calculated against the total amount of records in the database. For example, I count all my records that meet "MyCondition":
SELECT COUNT(*) FROM [MyTable]
WHERE Condition='MyCondition'
This works fine. However, how does one take that count, and return the percentage it equates to when put against all the records in the database? In other words, I want to see the percentage of how many records meet WHERE Condition='MyCondition' in regards to the total record count.
Sorry for the simple question and TIA! I am using MS SQL 2012.
Here is another method that only hits the base table once.
SELECT COUNT(*) as TotalCount
,SUM(case when Condition = 'MyCondition' then 1 else 0 end) as ConditionalCount
FROM [MyTable]
You can do simply divide the match of the count by the total number of records.
Sample Data:
create table test (MatchColumn int)
insert into test (MatchColumn)
values (1),(1),(1),(2),(3),(4)
Match Condition:
SELECT COUNT(*) MatchValues,
(SELECT COUNT(*) FROM test) TotalRecords,
CAST(COUNT(*) AS FLOAT)/CAST((SELECT COUNT(*) FROM test) AS FLOAT)*100 Percentage
FROM [test]
WHERE MatchColumn=1
Returns:
| MATCHVALUES | TOTALRECORDS | PERCENTAGE |
|-------------|--------------|------------|
| 3 | 6 | 50 |
SQL Fiddle Demo
Using a CTE:
Another option is to do the same with a CTE and reference the columns it creates:
;WITH CTE AS
(SELECT COUNT(*) MatchValues,
(SELECT COUNT(*) FROM test) TotalRecords
FROM [test]
WHERE MatchColumn=1)
SELECT MatchValues, TotalRecords,
CAST(MatchValues AS FLOAT)/CAST(TotalRecords AS FLOAT)*100 Percentage
FROM CTE
SQL Fiddle Demo
NOTE: Casting the counts to a float to calculate a percentage is required as dividing 2 int values would return an int, but in this case we want a decimal value less than 0, which would simply be 0 if it were an int.
Reference:
SQL Server, division returns zero
Or something like this.
create table test (MatchColumn int)
insert into test (MatchColumn)
values (1),(1),(1),(2),(3),(4)
SELECT
(SELECT COUNT(*) FROM test WHERE MatchColumn=1) MatchValues,
(SELECT COUNT(*) FROM test) TotalRecords,
((SELECT CAST(COUNT(*) AS FLOAT) FROM test WHERE MatchColumn=1)/CAST((SELECT COUNT(*) FROM test) AS FLOAT)*100) Percentage

Adding count in select query

I am trying to find a query that would give me a count of another table in the query. The problem is that I have no idea what to set where in the count part to. As it is now it will just give back a count of all the values in that table.
Select
ID as Num,
(select Count(*) from TASK where ID=ID(Also tried Num)) as Total
from ORDER
The goal is to have a result that reads like
Num Total
_________________
1 13
2 5
3 22
You need table aliases. So I think you want:
Select ID as Num,
(select Count(*) from TASK t where t.ID = o.ID) as Total
from ORDER o;
By the way, ORDER is a terrible name for a table because it is a reserved work in SQL.
You can do it as a sub query or a join (or an OVER statement.)
I think the join is clearest when you are first learning SQL
Select
ID as Num, count(TASK.ID) AS Total
from ORDER
left join TASK ON ORDER.ID=TASK.ID
GROUP BY ORDER.ID

SQL. Is there any efficient way to find second lowest value?

I have the following table:
ItemID Price
1 10
2 20
3 12
4 10
5 11
I need to find the second lowest price. So far, I have a query that works, but i am not sure it is the most efficient query:
select min(price)
from table
where itemid not in
(select itemid
from table
where price=
(select min(price)
from table));
What if I have to find third OR fourth minimum price? I am not even mentioning other attributes and conditions... Is there any more efficient way to do this?
PS: note that minimum is not a unique value. For example, items 1 and 4 are both minimums. Simple ordering won't do.
SELECT MIN( price )
FROM table
WHERE price > ( SELECT MIN( price )
FROM table )
select price from table where price in (
select
distinct price
from
(select t.price,rownumber() over () as rownum from table t) as x
where x.rownum = 2 --or 3, 4, 5, etc
)
Not sure if this would be the fastest, but it would make it easier to select the second, third, etc... Just change the TOP value.
UPDATED
SELECT MIN(price)
FROM table
WHERE price NOT IN (SELECT DISTINCT TOP 1 price FROM table ORDER BY price)
To find out second minimum salary of an employee, you can use following:
select min(salary)
from table
where salary > (select min(salary) from table);
This is a good answer:
SELECT MIN( price )
FROM table
WHERE price > ( SELECT MIN( price )
FROM table )
Make sure when you do this that there is only 1 row in the subquery! (the part in brackets at the end).
For example if you want to use GROUP BY you will have to define even further using:
SELECT MIN( price )
FROM table te1
WHERE price > ( SELECT MIN( price )
FROM table te2 WHERE te1.brand = te2.brand)
GROUP BY brand
Because GROUP BY will give you multiple rows, otherwise you will get the error:
SQL Error [21000]: ERROR: more than one row returned by a subquery used as an expression
I guess a simplest way to do is using offset-fetch filter from standard sql, distinct is not necessary if you don't have repeat values in your column.
select distinct(price) from table
order by price
offset 1 row fetch first 1 row only;
no need to write complex subqueries....
In amazon redshift use limit-fetch instead for ex...
Select distinct(price) from table
order by price
limit 1
offset 1;
You can either use one of the following:-
select min(your_field) from your_table where your_field NOT IN (select distinct TOP 1 your_field from your_table ORDER BY your_field DESC)
OR
select top 1 ColumnName from TableName where ColumnName not in (select top 1 ColumnName from TableName order by ColumnName asc)
I think you can find the second minimum using LIMIT and ORDER BY
select max(price) as minimum from (select distinct(price) from tableName order by price asc limit 2 ) --or 3, 4, 5, etc
if you want to find third or fourth minimum and so on... you can find out by changing minimum number in limit. you can find using this statement.
You can use RANK functions,
it may seem complex query but similar results like other answers can be achieved with the same,
WITH Temp_table AS (SELECT ITEM_ID,PRICE,RANK() OVER (ORDER BY PRICE) AS
Rnk
FROM YOUR_TABLE_NAME)
SELECT ITEM_ID FROM Temp_table
WHERE Rnk=2;
Maybe u can check the min value first and then place a not or greater than the operator. This will eliminate the usage of a subquery but will require a two-step process
select min(price)
from table
where min(price) <> -- "the min price you previously got"

Simple SQL select sum and values of same column

I have a co-worker who is working on a table with an 'amount' column.
They would like to get the top 5 amounts and the sum of the amounts in the same query.
I know you could do this:
SELECT TOP 5 amount FROM table
UNION SELECT SUM(amount) FROM table
ORDER BY amount DESC
But this produces results like this:
1000 (sum)
100
70
50
30
20
When what they really need is this:
100 | 1000
70 | 1000
50 | 1000
30 | 1000
20 | 1000
My intuitive attempts to achieve this tend to run into grouping problems, which isn't such an issue when you are selecting a different column, but is when you want to use an aggregate function based on the column you are selecting.
You can use a CROSS JOIN for this:
SELECT TOP 5 a.amount, b.sum
FROM table a
CROSS JOIN (SELECT SUM(amount) sum FROM table) b
ORDER BY amount DESC
This might work
SELECT TOP 5 amount, (SELECT SUM(amount) FROM table)
FROM table
ORDER BY amount DESC
Not really pretty, but this shouls do it:
SELECT TOP 5 amount, SAmount
FROM table Join
(SELECT SUM(amount) As SAmount FROM table)
ORDER BY amount DESC
As said by others, I'd probably use two queries.
Another approach using analytic functions (SQL Server 2005+):
SELECT TOP 5 amount, SUM(amount) OVER()
FROM table
ORDER BY
amount DESC