Mysql query for better sorting of result - sql

The mysql table that I'm using has a structure:
id | type | date
--------------------------------
101 | 1 | 2011-02-08
102 | 2 | 2011-02-08
103 | 2 | 2011-02-08
104 | 2 | 2011-02-07
105 | 1 | 2011-02-07
105 | 1 | 2011-02-07
What I want to do is create a query that will give the following result:
total type 1 | total type 2 | date
------------------------------------------------
1 | 2 | 2011-02-08
2 | 1 | 2011-02-07
I tried with following query but not getting the desired result
SELECT count(DISTINCT id) as total, date, type FROM my_table WHERE type !='0' GROUP BY date, type ORDER BY date DESC
How can I do that?
Thanks for all suggestions

Use:
SELECT t.date,
SUM(CASE WHEN t.type = 1 THEN 1 ELSE 0 END) AS total_type_1,
SUM(CASE WHEN t.type = 2 THEN 1 ELSE 0 END) AS total_type_2
FROM YOUR_TABLE t
WHERE t.type != 0
GROUP BY t.date
ORDER BY t.date DESC
I'm assuming the type column is numeric, not string based like the single quotes suggest. Change to suit if that's not the case.

Try this:
SELECT type, COUNT(*) as total_type, date FROM my_table WHERE type != '0' GROUP BY type ORDER BY date DESC

What you're asking for is pivot functionality, which I guess mysql isn't great at.
If it works for you, I'd suggest altering the output to:
type count date
==== ===== ====
1 1 2011-02-08
....
and rearrange at application logic. It would probably also make your SQL more db-independent.

Related

Calculate total time spent by group and one datetime column

I have a workflow application where the workflow is written to the DB as shown below when the status changes. There is no end time as it is a sequence of events. I want to create a query that will group by the WorkFlowID and total the amount of time spent in each. I am not sure how to even begin
My table and data looks like this
+------------+---------------------+
| WorkFlowID | EventTime |
+------------+---------------------+
| 1 | 07/15/2015 12:00 AM |
| 2 | 07/15/2015 12:10 AM |
| 3 | 07/15/2015 12:20 AM |
| 2 | 07/15/2015 12:30 AM |
| 3 | 07/15/2015 12:40 AM |
| 4 | 07/15/2015 12:50 AM |
+------------+---------------------+
My end result should be like:
+------------+-----------------+
| WorkFlowID | TotalTimeInMins |
+------------+-----------------+
| 1 | 10 |
| 2 | 20 |
| 3 | 20 |
| 4 | 10 |
+------------+-----------------+
In SQL Server 2012+, you would just use lead(). There are several ways to approach this in SQL Server 2008. Here is one using `outer apply:
select t.WorkFlowId,
sum(datediff(second, EventTime, nextTime)) / 60.0 as NumMinutes
from (select t.*, t2.EventTime as nextTime
from table t outer apply
(select top 1 t2.*
from table t2
where t2.EventTime > t.EventTime
order by t2.EventTime
) t2
) tt
group by t.WorkFlowId;
The only question is how you get "10" for event 4. There is no following event, so that value doesn't make sense. You can use datediff(second, EventTime coalesce(NextEvent, getdate()) to handle the NULL value.
As an alternative:
;WITH t AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY EventTime) As rn
FROM
yourTable)
SELECT
t1.WorkFlowID,
SUM(DATEDIFF(SECOND, t1.EventTime, ISNULL(t2.EventTime, GETDATE()) / 60) As TotalTimeInMins
FROM t t1
LEFT JOIN t t2
ON t1.rn = t2.rn - 1
The basis of a method that works in all (ok, I don't know about SQL 6.5) editions is to use the group by clause:
SELECT
WorkFlowID
,datediff(mi, min(EventTime), max(EventTime)) TotalTimeInMins
from MyTable
group by WorkFlowID
This does indeed leave the question of how you got 10 minutes with a start time and (presumably) no end time. As written, this query would list the
WorkFlowID with TotalTimeInMins = 0, which seems accurate enough. The following variant would remove all "start-only" items:
SELECT
WorkFlowID
,datediff(mi, min(EventTime), max(EventTime)) TotalTimeInMins
from MyTable
group by WorkFlowID
having count(*) > 1
(The quick explanation: having is to group by as where is to from)

subtract data from single column

I have a database table with 2 columns naming piece and diff and type.
Here's what the table looks like
id | piece | diff | type
1 | 20 | NULL | cake
2 | 15 | NULL | cake
3 | 10 | NULL | cake
I want like 20 - 15 = 5 then 15 -10 = 5 , then so on so fort with type as where.
Result will be like this
id | piece | diff | type
1 | 20 | 0 | cake
2 | 15 | 5 | cake
3 | 10 | 5 | cake
Here's the code I have so far but i dont think I'm on the right track
SELECT
tableblabla.id,
(tableblabla.cast(pieces as decimal(7, 2)) - t.cast(pieces as decimal(7, 2))) as diff
FROM
tableblabla
INNER JOIN
tableblablaas t ON tableblabla.id = t.id + 1
Thanks for the help
Use LAG/LEAD window function.
Considering that you want to find Difference per type else remove Partition by from window functions
select id, piece,
Isnull(lag(piece)over(partition by type order by id) - piece,0) as Diff,
type
From yourtable
If you are using Sql Server prior to 2012 use this.
;WITH cte
AS (SELECT Row_number()OVER(partition by type ORDER BY id) RN,*
FROM Yourtable)
SELECT a.id,
a.piece,
Isnull(b.piece - a.piece, 0) AS diff,
a.type
FROM cte a
LEFT JOIN cte b
ON a.rn = b.rn + 1

Redshift: Getting rank of a row, filtered by a condition

Every time I add a row to a table, I want to know where it ranks in comparison with the table up to that point. This is easily done with the RANK() window function. However, I'm struggling to find a way to to discover where it ranks in comparison with the table up until that point filtered by a value.
As an example, I'm wanting to end up with this highly contrived table:
date | name | animal_bought | num_sloths_bought_before | num_camels_bought_before
------------+---------+---------------+--------------------------+--------------------------
2014-09-01 | Vincent | sloth | 0 | 0
2014-09-01 | Luis | camel | 0 | 0
2014-09-02 | Vincent | sloth | 1 | 0
2014-09-02 | Luis | camel | 0 | 1
2014-09-02 | Kevin | sloth | 0 | 0
2014-09-03 | Vincent | camel | 1 | 0
2014-09-04 | Deo | camel | 0 | 0
2014-09-04 | Vincent | sloth | 2 | 1
2014-09-05 | Luis | camel | 0 | 2
2014-09-05 | Andrew | sloth | 0 | 0
I was initially looking to see whether I could apply a filter to the window function (eg. RANK() OVER(PARTITION BY name WHERE animal_bought = 'sloth' ORDER BY date ASC) AS num_sloths_bought_before) but this isn't syntactically correct. I then tried adding a sub-query, as follows:
SELECT
date,
name,
animal_bought,
( SELECT
RANK() OVER(PARTITION BY name ORDER BY date ASC) - 1
FROM this_table
WHERE animal_bought = 'sloth'
) AS num_sloths_bought_before
FROM source_table
but Redshift threw this error:
ERROR: This type of correlated subquery pattern is not supported yet
I've also tried putting the window function in a case statement (throws the same error) and calculating the ranks in a join query (not been able to make it work).
Hmmm. I don't think this query would do what you want anyway:
SELECT date, name, animal_bought,
(SELECT RANK() OVER(PARTITION BY name ORDER BY date ASC) - 1
FROM this_table
WHERE animal_bought = 'sloth'
) AS num_sloths_bought_before
FROM source_table
For a few reasons:
The use of rank() suggests that there is more than one row in this_table that matches animal_bought. Otherwise, you could use an aggregation function.
If there is only one row that matches the where clause, then the value is always 1, because the where clause is processed before the rank().
Your question only mentions one table but your query has two
Perhaps you just want rank() without a subquery?
SELECT date, name, animal_bought,
RANK() OVER (PARTITION BY name, animal ORDER BY date ASC) - 1 as NumberBoughtBefore
FROM source_table;
If you want it for both animals, then don't use rank(), use cumulative sum:
SELECT date, name, animal_bought,
sum(case when animal = 'sloth' then 1 else 0 end) over (partition by name order by date) as SlothsBefore,
sum(case when animal = 'camel' then 1 else 0 end) over (partition by name order by date) as CamelsBefore
FROM source_table;
EDIT:
SELECT date, name, animal_bought,
(sum(case when animal = 'sloth' then 1 else 0 end) over (partition by name order by date) -
(case when animal = 'sloth' then 1 else 0 end)
) as SlothsBefore,
(sum(case when animal = 'camel' then 1 else 0 end) over (partition by name order by date) -
(case when animal = 'camel' then 1 else 0 end)
) as CamelsBefore
FROM source_table;

postgres - having group by min of date time and status

I would like to retrieve car_id from the below table which have
min (svc_date) with status=A .
expected is 103 and 100 which have status 'A'
because it have the status A with minimum svc_date
car_id svc_date status
100 03/01/2013 A
100 04/02/2013 B
100 05/03/2013 C
101 06/01/2013 A
101 05/01/2013 B
102 06/06/2013 A
102 05/05/2013 B
103 05/25/2013 A
i am using postgres and am trying
select car_id,svc_date,status from car_tbl group by
car_id having min(svc_date) and status='A'
svc_date is timestamp with timezone and
getting error
argument of AND must be type boolean, not type timestamp with time zone
is the entire sql is wrong or any type cast will help?
Really, it is hard to understand to you:
with cte as (
select
car_id ,
status,
rank() OVER (
PARTITION BY car_id
ORDER BY svc_date )
from car_tbl
)
select *
from cte
where rank = 1
and status = 'A'
Results:
| CAR_ID | STATUS | RANK |
--------------------------
| 100 | A | 1 |
| 103 | A | 1 |
It's rather difficult to see what you're really asking; but does this do what you want?
SELECT car_id, status, MIN(svc_date) FROM car_tbl WHERE status = 'A' GROUP BY car_id, status

SQL check if group contains NULL

Is there any function to check if a column in a group contains a NULL, alternatively how would I solve this? Example below of data structure.
id | value
----------
1 | NULL
1 | 56
2 | 98
2 | 14
Result:
id | value
----------
1 | 1
2 | 0
try
select id,
count(*) - count(value) as null_value_count
from your_table
group by id
SQLFiddle demo
Another possibility which doesn't use the fact that count(value) ignores NULL values:
select id,
sum(case when value is null then 1 else 0 end) as null_count
from your_table
group by id;