Oracle/SQL Couting multiple colums grouped by a common column - sql

I'm back with yet another Oracle query. What I want to do is do counting on multiple columns grouped by a common field. I have half of this done so far. So given the following table
THING ACTION
--------------
T1 _A_
T1 _A_
T1 _B_
T2 _A_
T2 _B_
I have this query
select THING,
count(ACTION) as "A"
from <table>
where ACTION = '_A_'
group by THING
Which results in
THING A
----------
T1 2
T2 1
What I would like to see though is this
THING A B
--------------
T1 2 1
T2 1 1
But I'm not certain how to do that. Any ideas?
Thanks!

select thing,
count(case action when '_A_' then 1 end) as a,
count(case action when '_B_' then 1 end) as b
from <table>
group by thing
or sum(case action when '_A_' then 1 else 0 end) if you prefer

Related

SQL: status flips between 1 and 2; select all statuses, which are 2 since the last time it was 1

I have write only DB log of changes I keep track of (or statuses) and values fluctuate between 1 and 2. In the below table; ID is identity column, STATUS is either 1 or 2 and USER is a user id.
If the latest status (i.e. max ID) for a given user is 1 then my query should return nothing (1 = good). So running the query against the data above would be just that.
Here comes my question: I want to query for all statuses of 2 since the last time it was 1. Here is sample data:
In this case my query should return 2 and 3 (ID) because these have statuses of 2 since the last time it was 1.
This next query should return nothing because the latest status for this user was switched to 1:
And finally this next one should return 5 (because the latest status is 2 since the last time it was 1):
There is no date field in this table, you can only work with MAX(ID) ... GROUP BY ID, USER
How can I do this? I'm using MS SQL 2016.
You can use windowed aggregates to do this
WITH T
AS (SELECT ID,
STATUS,
[USER],
MAX(CASE WHEN Status = 1 THEN ID END) OVER ( PARTITION BY [USER]) AS MaxS1
FROM YourTable)
SELECT *
FROM T
WHERE Status = 2
AND (ID > MaxS1 OR MaxS1 IS NULL)
Remove the OR MaxS1 IS NULL if you don't want the rows returned for users that have 2 and have never had 1 as a status
You can filter with not exists:
select t.*
from mytable t
where
t.status = 2
and not exists (
select 1 from mtyable t1 where t1.user = t.user and t1.id > t.id and t1.status = 1
)
This phrases as: all records with status 2 that have no following record (ie a record with the same user and a greatest id) with status = 1. If there are no records with status = 1 for a given user, all its records will be returned.
This can also be expressed with a left join antipattern:
select t.*
from mytable t
left join mytable t1 on t1.user = t.user and t1.id > t.id and t1.status = 1
where t1.id is null and t.status = 2

How to use multiple counts in where clause to compare data of a table in sql?

I want to compare data of a table with its other records. The count of rows with a specific condition has to match the count of rows without the where clause but on the same grouping.
Below is the table
-------------
id name time status
1 John 10 C
2 Alex 10 R
3 Dan 10 C
4 Tim 11 C
5 Tom 11 C
Output should be time = 11 as the count for grouping on time column is different when a where clause is added on status = 'C'
SELECT q1.time
FROM (SELECT time,
Count(id)
FROM table
GROUP BY time) AS q1
INNER JOIN (SELECT time,
Count(id)
FROM table
WHERE status = 'C'
GROUP BY time) AS q2
ON q1.time = q2.time
WHERE q1.count = q2.count
This is giving the desired output but is there a better and efficient way to get the desired result?
Are you looking for this :
select t.*
from table t
where not exists (select 1 from table t1 where t1.time = t.time and t1.status <> 'C');
However you can do :
select time
from table t
group by time
having sum (case when status <> 'c' then 1 else 0 end ) = 0;
If you want the times where the rows all satisfy the where clause, then in Postgres, you can express this as:
select time
from t
group by time
having count(*) = count(*) filter (where status = 'C');

SQL - for each entry in a table - check for associated row

I have a log table which logs a start row, and a finish row for a particular event.
Each event should have a start row, and if everything goes ok it should have an end row.
But if something goes wrong then the end row may not be created.
I want to SELECT everything in the table that has a start row but not an associated end row.
For example, consider the table like this:
id event_id event_status
1 123 1
2 123 2
3 234 1
4 234 2
5 456 1
6 678 1
7 678 2
Notice that the id column 5 has a start row but no end row. Start is an event_status of 1, end is an event_status of 2.
How can i pull back all the event_ids which have a start row but not an end row>?
This is for mssql.
You could use a not exists subquery to demand that no other row exists that ends the event:
select *
from YourTable t1
where status = 1
and not exists
(
select *
from YourTable t2
where t2.event_id = t1.event_id
and t2.status = 2
)
You can try with left self join as below:
select y1.event_id from #yourevents y1 left join #yourevents y2
on y1.event_id = y2.event_id
and y1.event_status = 1
and y2.event_status = 2
where y2.event_id is null
and y1.event_status = 1
In this particular case you could use one of 3 solutions:
Solution 1. The classic
Check if there is no end status
SELECT *
FROM myTable t1
WHERE NOT EXISTS (
SELECT *
FROM myTable t2
WHERE t1.event_id = t2.event_id AND t2.status=2
)
Solution 2. Make it pretty. Don't do subqueries with so many parentheses
The same check, but in a more concise and pretty manner
SELECT t1.*
FROM myTable t1
LEFT JOIN myTable t2 ON t1.event_id = t2.event_id AND t2.status=2
-- Doesn't exist
WHERE t2.event_id IS NULL
Solution 3. Look for the last status for each event
More flexibility in case the status logic becomes more complicated
WITH last_status AS (
SELECT
id,
event_id,
status,
-- The ROWS BETWEEN ..yadda yadda ... FOLLOWING might be unnecessary. Try, check.
last_value(status) OVER (PARTITION BY event_id ORDER BY status ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_status
FROM myTable
)
SELECT
id,
event_id,
status
FROM last_events
WHERE last_status<>2
There are more, with min/max queries and others. Pick what best suits your need for cleanliness, readability and versatility.

Add Column values in sql server query

I have result of two queries like:
Result of query 1
ID Value
1 4
2 0
3 6
4 9
Result of query 2
ID Value
1 6
2 4
3 0
4 1
I want to add values column "Value" and show final result:
Result of Both queries
ID Value
1 10
2 4
3 6
4 10
plz guide me...
select id, sum(value) as value
from (
select id, value from query1
uninon all
select id, value from query2
) x
group by id
Try using a JOIN:
SELECT
T1.ID,
T1.Value + T2.Value AS Value
FROM (...query1...) AS T1
JOIN (...query2...) AS T2
ON T1.Id = T2.Id
You may also need to consider what should happen if there is an Id present in one result but not in the other. The current query will omit it from the results. You may want to investigate OUTER JOIN as an alternative.
A not particularly nice but fairly easy to comprehend way would be:
SELECT ID,SUM(Value) FROM
(
(SELECT IDColumn AS ID,ValueColumn AS Value FROM TableA) t1
OUTER JOIN
(SELECT IDColumn AS ID,ValueColumn AS Value FROM TableB) t2
) a GROUP BY a.ID
It has the benefits of
a) I don't know your actual table structure so you should be able to work out how to get the two 'SELECT's working from your original queries
b) If ID doesn't appear in either table, that's fine

Crosstab/Pivot query in TSQL on nvarchar columns

I have a Table1:
ID Property
1 Name
2 City
3 Designation
and Table2:
ID RecordID Table1ID Value
1 1 1 David
2 1 2 Tokyo
3 2 1 Scott
4 2 3 Manager
The Table1ID of Table2 maps to Table1's ID. Now I wish to show the Table1 Property column values as column headers and have a result set in format like:
RecordID Name City Designation
1 David Tokyo NULL
2 Scott NULL Manager
What is the best/efficient way to achieve this in T-SQL considering that the number of records in Table1 (i.e. the columns in result set) can change and thus should be handled dynamically.
Although I tried PIVOT and CASE based queries, but have been struggling with both of them. :(
Any help/guidance would be appreciated.
Thanks!
Update:
I've been able to create the dynamic query, but one thing which I am still not able to understand is why MAX has been used in the CASE statements. Kindly ignore my noobness.
Use:
SELECT t2.recordid,
MAX(CASE WHEN t1.property = 'Name' THEN t2.value END) AS name,
MAX(CASE WHEN t1.property = 'City' THEN t2.value END) AS city,
MAX(CASE WHEN t1.property = 'Designation' THEN t2.value END) AS designation
FROM TABLE2 t2
JOIN TABLE1 t1 ON t1.id = t2.table1id
GROUP BY t2.recordid
ORDER BY t2.recordid