SQL: Get dates with two specific events - sql

Table:
Logs
Fields:
LogDate datetime,
ErrId int
ErrId 9 and 5 sometimes occur on same date.
How can I find those dates?
Of course the task can be about any pair of ErrIds not just 9 and 5.
I ended up with following sql statement:
select distinct l_1.LogDate
from logs l_1
where exists (select * from logs l_2 where l_1.LogDate = l_2.LogDate and l_2.ErrId = 9)
and exists (select * from logs l_3 where l_1.LogDate = l_3.LogDate and l_3.ErrId = 5)
The question: is there better solution for the task?

You could use a self-join like this:
select distinct l_1.LogDate
from logs l_1
inner join logs l_2 on l_1.LogDate = l_2.LogDate
where l_1.ErrId = 9
and l_2.ErrId = 5
Note that generally it is better to use group by instead of distinct. Give it a try:
select l_1.LogDate
from logs l_1
inner join logs l_2 on l_1.LogDate = l_2.LogDate
where l_1.ErrId = 9
and l_2.ErrId = 5
group by l_1.LogDate

Perhaps the easiest method is simply group by with having:
select l.LogDate
from logs l
group by l.log_date
having sum(case when l.ErrId = 9 then 1 else 0 end) > 0 and
sum(case when l.ErrId = 5 then 1 else 0 end) > 0;
If you are only looking for a fixed set of values, you can also write this as:
select l.LogDate
from logs l
where l.ErrId in (5, 9)
group by l.log_date
having count(distinct l.ErrId) = 2;
Both of these should be faster than doing a group by/distinct along with a join.

Related

SQL query help: How to evaluate value/max(value) based on some conditions in same query

Not an expert in SQL. I am using postgres database with EF migration. Stuck with this requirement. Here it goes.
My table is like this:
A B C D
20 1 1 1
59 0 0 1
57 1 1 1
10 1 0 0
30 1 1 1
15 0 0 0
The order of rows is like oldest to latest(top to bottom).
Half query I have from my project is as below:
SELECT dcr."A"
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."B" != 0
ORDER BY "UtcDate" desc
limit(1)
This will fetch me the first part value of latest A when it matches condition. But not the Max part and the division part as explained below.
I want to find the result of ((latest A where B = C = D = 1 divided by max of A in its previous rows where B = C = D = 1) - 1) * 100.
I want this to happen in single query and there are multiple groups like this. Lets say the table contains around 60 rows and we can group them based on some other column. Each group should evaluate this above formula.
Expected result for above example should be:
result = ((30 / 57) - 1) * 100 = (0.5263 - 1) * 100 = -47.73
You can use a subquery to get the max. I don't know why you're writing the query in that strange style, but I will keep it:
SELECT dcr."A" / (SELECT MAX("A")
FROM "DCR"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0)) - 1) * 100
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0
ORDER BY "UtcDate" desc
limit(1)
maybe something like this?
select (t1."A"/max(t2."A"))*100 from
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t1
join
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t2
on t1.id>t2.id
group by t1."A",t1."E"

Union different tables with differents columns and data

My problem is that I have 4 differents SELECT with
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
I want that the results appear together whith the respective names like:
regular rptmm new rptss
10 5 2 6
Firstly, I'd suggest not to use Count()*. There are many answers on this site explaining why so I am not going to repeat it.
Instead, I'd suggest you to use a query like this:
SELECT (SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 1) AS 'Regular',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 0) AS 'rptmm',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 0) AS 'New',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 1) AS 'rptss'
Hope this helps!!!
Just put UNION ALL between your four statements you will get four rows with each count on its own row. However, you will lose the column name. You could also use join to get one row with four columnes. Just put the keyword join between each sql statement.
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
JOIN
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
JOIN
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
JOIN
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
You could create a temp table to hold all of this data for you: Replace Name1, Name2, Name3,Name4 with whatever you want to call them. These will be the column headers.
CREATE TABLE #Temp(
NAME1 INT
,NAME2 INT
,NAME3 INT
,NAME4 INT
)
INSERT INTO #Temp
(NAME1)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 1
INSERT INTO #Temp
(NAME2)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 0
INSERT INTO #Temp
(NAME3)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 0
INSERT INTO #Temp
(NAME4)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 1*
SELECT * FROM #Temp

SQLite3: Return a NULL if no records exist in SUM()

I would like to SUM() while also using a WHERE but when there are no records found for a certain ID I would like it to return NULL instead of just not returning anything.
Initial Code:
SELECT
ID,
SUM(CASE WHEN EVENTS = 3 THEN 1 ELSE 0 END)
FROM Events_ID
WHERE
YEAR = 2012
GROUP BY ID
This would not return an ID if there were no events for it in 2012.
I then changed it to the following that appears to work but is around 100x slower!
SELECT
ID,
(SELECT
SUM(CASE WHEN EVENTS = 3 THEN 1 ELSE 0 END)
FROM EVENTS_ID r WHERE r.ID = t.ID AND r.YEAR = 2012)
FROM (SELECT * FROM Events_ID GROUP BY ID) as t;
Is there anyway to get the output of the second query nearer to the speed of the first?
Is this what you want?
SELECT ID,
SUM(CASE WHEN EVENTS = 3 AND YEAR = 2012 THEN 1 END)
FROM Events_ID
GROUP BY ID;
This will return all ids, with a NULL as a second value if no events match both conditions.

2 Conditions for the same column in a SQl query

How can i get the distinct dates in a table where R.TypeofDayID = 2 and it should not include dates with R.TypeofDayID = 1
I can get all the distinct dates for R.TypeofDayID = 2 using the below query but i am not sure how i can add a condition to get dates where TypeofDayID = 2 and the date should not have TypeofDayID = 1 in any row.
Select count(distinct(R.Date)) from RepInfo R
where R.TypeofDayID = 2 and Month(R.Date) = 2 and Year(R.Date) = 2013
I hope i am clear. This is in SQL Server 2005. Thank you in advance!
Select R.Date, count(*) as cnt
from RepInfo R
where Month(R.Date) = 2 and Year(R.Date) = 2013
group by R.Date
having sum(case when TypeofDayID <> 2 then 1 else 0 end) = 0
Why are you using a COUNT() function in your select statement if you want the the output to be a list of distinct dates? Would you get your desired result from changing your select to something like this:
Select distinct(R.Date)

SQL Count with multiple conditions then join

Quick one,
I have a table, with the following structure
id lid taken
1 1 0
1 1 0
1 1 1
1 1 1
1 2 1
Pretty simply so far right?
I need to query the taken/available from the lid of 1, which should return
taken available
2 2
I know I can simply do two counts and join them, but is there a more proficient way of doing this rather than two separate queries?
I was looking at the following type of format, but I can not for the life of me get it executed in SQL...
SELECT
COUNT(case taken=1) AS taken,
COUNT(case taken=0) AS available FROM table
WHERE
lid=1
Thank you SO much.
You can do this:
SELECT taken, COUNT(*) AS count
FROM table
WHERE lid = 1
GROUP BY taken
This will return two rows:
taken count
0 2
1 2
Each count corresponds to how many times that particular taken value was seen.
Your query is correct just needs juggling a bit:
SELECT
SUM(case taken WHEN 1 THEN 1 ELSE 0 END) AS taken,
SUM(case taken WHEN 1 THEN 0 ELSE 1 END) AS available FROM table
WHERE
lid=1
Alternatively you could do:
SELECT
SUM(taken) AS taken,
COUNT(id) - SUM(taken) AS available
FROM table
WHERE
lid=1
SELECT
SUM(case WHEN taken=1 THEN 1 ELSE 0 END) AS taken,
SUM(case WHEN taken=0 THEN 1 ELSE 0 END) AS available
FROM table
WHERE lid=1
Weird application of CTE's:
WITH lid AS (
SELECT DISTINCT lid FROM taken
)
, tak AS (
SELECT lid,taken , COUNT(*) AS cnt
FROM taken t0
GROUP BY lid,taken
)
SELECT l.lid
, COALESCE(a0.cnt, 0) AS available
, COALESCE(a1.cnt, 0) AS taken
FROM lid l
LEFT JOIN tak a0 ON a0.lid=l.lid AND a0.taken = 0
LEFT JOIN tak a1 ON a1.lid=l.lid AND a1.taken = 1
WHERE l.lid=1
;