SQL Comparing 2 columns then changing the values in one dynamically - sql

I have a Query where i want it to display a range of dates, and in the next column have a count of when an event happens on a day.
At the minute i can display how many times an event happens on a day, but when i try to display all of the dates, they all display that 1 event happens on it.
I've tried doing Case's and If statements, but seem to get syntax errors
select distinct d.DateNumber, count(*) as AmountofEvents
from dbo.dateTable d
join dbo.TimeTaken m
on d.dateNumber = m.dateEventHappens or d.dateNumber != m.dateEventHappens
group by d.dateNumber, m.dateEventHappens
Order by d.dateValue ASC
DateNumber is the list of the dates, AmountofEvents is the count of events on that day and dateEventHappens is the day that has the event.
I need to get a way to compare when the dateNumber is not equal to dateEventHappens, and then display the AmountofEvents count as 0 for that day.

You need to fix the GROUP BY, remove the SELECT DISTINCT, and introduce LEFT JOIN:
select d.DateNumber, count(m.dateEventHappens) as AmountofEvents
from dbo.dateTable d left join
dbo.TimeTaken m
on d.dateNumber = m.dateEventHappens
group by d.dateNumber
order by d.dateValue ASC;

Related

How can I create a query in SQL Server, using as base table a date function and linking it to another table?

I am trying to create a query using a function of dates and a table of shifts, which can show me the shifts of workers each day, when I have a shift or rest depending on the day,
What do I have: I have a date function that gives me a range of dates that I add, I attach an example:
I have a table of shifts, with only the days that a person has a shift, if a day has a break, the date or the row does not appear, I attach an example:
It can be seen that in the shift table there are only records when a person has a shift.
Problem: when I perform the join between the function and the shift table through the date field, the result is that it only shows me the record when it has a shift and no, it does not put the date when it has a break, I attach an example:
Desired result:
The idea is that when the worker has a break, the row will be blank, only showing the date and his ID, or saying the word break.
I hope you can help me. Thank you so much.
Use LEFT JOIN for avoiding few date missing which has transaction in table.
Use two subquery here for getting appropriate result. In first subquery function CROSS JOIN with transaction table where retrieving distinct id_trabajador for specified date range. If it doesn't do then id will blank in result where no transaction exists for specific id in a certain date. In second subquery retrieve all rows for given date range.
-- SQL Server
SELECT tmp.fecha, tmp.id_trabajador
, tmd.inicio, tmd.termino
, COALESCE(CAST(tmd.jornada AS varchar(20)), 'DESCANSO') jornada
FROM (SELECT * FROM shift_cmr..fnRangoFechas('01-sep-2021', '31-dec-2021') t
CROSS JOIN (SELECT id_trabajador
FROM shift_cmr..trabajadores_turnos_planificados
WHERE fecha BETWEEN '2021-09-01' AND '2021-12-31'
GROUP BY id_trabajador) tt
) tmp
LEFT JOIN (SELECT *
FROM shift_cmr..trabajadores_turnos_planificados
WHERE fecha BETWEEN '2021-09-01' AND '2021-12-31') tmd
ON tmp.fecha = tmd.fecha
AND tmp.id_trabajador = tmd.id_trabajador
You need to start with the date table and LEFT JOIN everything else
SELECT
dates.fecha,
sh.id_trabajador,
sh.inicio,
sh.termino,
jornada = ISNULL(CAST(sh.jornada AS varchar(10)), 'DESCANSO')
FROM shift_cmr..fnRangoFechas('01-sep-2021', '31-dec-2021') dates
LEFT JOIN shift_cmr..trabajadores_turnos_planificados sh
ON sh.fecha = dates.fecha
This only gives you one blank row per date. If you need a blank row for every id_trabajador then you need to cross join that
SELECT
dates.fecha,
t.id_trabajador,
sh.inicio,
sh.termino,
jornada = ISNULL(CAST(sh.jornada AS varchar(10)), 'DESCANSO')
FROM shift_cmr..fnRangoFechas('01-sep-2021', '31-dec-2021') dates
CROSS JOIN shift_cmr..trabajadores t -- guessing the table name
LEFT JOIN shift_cmr..trabajadores_turnos_planificados sh
ON sh.fecha = dates.fecha AND t.id_trabajador = sh.id_trabajador

Query to return the matching or nearest previous record by a list of dates

I have a table of records ordered by date. There is a maximum of 1 record per day, but some days there is no record (weekends and bank holidays).
When I query a record by date, if no record exists for that day I am interested in the previous record by date. Eg:
SELECT * FROM rates WHERE date <= $mydate ORDER BY date DESC LIMIT 1;
Given a list of dates, how would I construct a query to return multiple records matching the exact or closest previous record for each date? Is this possible to achieve in a single query?
The array of dates may be spread over a large time frame but I wouldn't necessarily want every record in the entire time span (eg query 20 dates spread over a year long time span).
You can construct the dates as a derived table and then use SQL logic. A lateral join is convenient:
select v.dte, r.*
from (values ($date1), ($date2), ($date3)
) v(dte) left join lateral
(select r.*
from rates r
where r.date <= v.dte
order by r.date desc
limit 1
) r
on 1=1;
You might find it useful to use an array to pass in the dates using an array and using unnest() on that array.

SQL-How to Sum Data of Clients Over Time?

Goal: SUM/AVG Client Data over multiple dates/transactions.
Detailed Question: How do I properly Group clients ('PlayerID') then SUM the int(MinsPlayed), then AVG (AvgBet)?
Current Issue: my Results are giving individual transactions day by day over the 90 day time period instead of the SUM/AVG over the 90 days.
Current Script/Results: FirstName-Riley is showing each individual daily transaction instead of 1 total SUM/AVG over set time period
Firstly, you don't need to use DISTINCT as you are going to be aggregating the results using GROUP BY, so you can take that out.
The reason you are returning a row for each transaction is that your GROUP BY clause includes the column you are trying to aggregate (e.g. TimePlayed). Typically, you only want to GROUP BY the columns that are not being aggregated, so remove all the columns from the GROUP BY clause that you are aggregating using SUM or AVG (TimePlayed, PlayerSkill etc.).
Here's your current SQL:
SELECT DISTINCT CDS_StatDetail.PlayerID,
StatType,
FirstName,
LastName,
Email,
SUM(TimePlayed)/60 AS MinsPlayed,
SUM(CashIn) AS AvgBet,
SUM(PlayerSkill) AS AvgSkillRating,
SUM(PlayerSpeed) AS Speed,
CustomFlag1
FROM CDS_Player INNER JOIN CDS_StatDetail
ON CDS_Player.Player_ID = CDS_StatDetail.PlayerID
WHERE StatType='PIT' AND CDS_StatDetail.GamingDate >= '1/02/17' and CDS_StatDetail.GamingDate <= '4/02/2017' AND CustomFlag1='N'
GROUP BY CDS_StatDetail.PlayerID, StatType, FirstName, LastName, Email, TimePlayed, CashIn, PlayerSkill, PlayerSpeed, CustomFlag1
ORDER BY CDS_StatDetail.PlayerID
You want something like:
SELECT CDS_StatDetail.PlayerID,
SUM(TimePlayed)/60 AS MinsPlayed,
AVG(CashIn) AS AvgBet,
AVG(PlayerSkill) AS AvgSkillRating,
SUM(PlayerSpeed) AS Speed,
FROM CDS_Player INNER JOIN CDS_StatDetail
ON CDS_Player.Player_ID = CDS_StatDetail.PlayerID
WHERE StatType='PIT' AND CDS_StatDetail.GamingDate BETWEEN '2017-01-02' AND '2017-04-02' AND CustomFlag1='N'
GROUP BY CDS_StatDetail.PlayerID
Next time, please copy and paste your text, not just linking to a screenshot.

Get a Row if within certain time period of other row

I have a SQL statement that I am currently using to return a number of rows from a database:
SELECT
as1.AssetTagID, as1.TagID, as1.CategoryID,
as1.Description, as1.HomeLocationID, as1.ParentAssetTagID
FROM Assets AS as1
INNER JOIN AssetsReads AS ar ON as1.AssetTagID = ar.AssetTagID
WHERE
(ar.ReadPointLocationID='Readpoint1' OR ar.ReadPointLocationID='Readpoint2')
AND (ar.DateScanned between 'LastScan' AND 'Now')
AND as1.TagID!='000000000000000000000000'
I am wanting to do a query that will get the row with the oldest DateScanned from this query and also get another row from the database if there was one that was within a certain period of time from this row (say 5 seconds for an example). The oldest record would be relatively simple by selecting the first record in a descending sort, but how would I also get the second record if it was within a certain time period of the first?
I know I could do this process with multiple queries, but is there any way to combine this process into one query?
The database that I am using is SQL Server 2008 R2.
Also please note that the DateScanned times are just placeholders and I am taking care of that in the application that will be using this query.
Here is a fairly general way to approach it. Get the oldest scan date using min() as a window function, then use date arithmetic to get any rows you want:
select t.* -- or whatever fields you want
from (SELECT as1.AssetTagID, as1.TagID, as1.CategoryID,
as1.Description, as1.HomeLocationID, as1.ParentAssetTagID,
min(DateScanned) over () as minDateScanned, DateScanned
FROM Assets AS as1
INNER JOIN AssetsReads AS ar ON as1.AssetTagID = ar.AssetTagID
WHERE (ar.ReadPointLocationID='Readpoint1' OR ar.ReadPointLocationID='Readpoint2')
AND (ar.DateScanned between 'LastScan' AND 'Now')
AND as1.TagID!='000000000000000000000000'
) t
where datediff(second, minDateScanned, DateScanned) <= 5;
I am not really sure of sql server syntax, but you can do something like this
SELECT * FROM (
SELECT
TOP 2
as1.AssetTagID,
as1.TagID,
as1.CategoryID,
as1.Description,
as1.HomeLocationID,
as1.ParentAssetTagID ,
ar.DateScanned,
LAG(ar.DateScanned) OVER (order by ar.DateScanned desc) AS lagging
FROM
Assets AS as1
INNER JOIN AssetsReads AS ar
ON as1.AssetTagID = ar.AssetTagID
WHERE (ar.ReadPointLocationID='Readpoint1' OR ar.ReadPointLocationID='Readpoint2')
AND (ar.DateScanned between 'LastScan' AND 'Now')
AND as1.TagID!='000000000000000000000000'
ORDER BY
ar.DateScanned DESC
)
WHERE
lagging IS NULL or DateScanned - lagging < '5 SECONDS'
I have tried to sort the results by DateScanned desc and then just the top most 2 rows. I have then used the lag() function on DateScanned field, to get the DateScanned value for the previous row. For the topmost row the DateScanned shall be null as its the first record, but for the second one it shall be value of the first row. You can then compare both of these values to determine whether you wish to display the second row or not
more info on the lagging function: http://blog.sqlauthority.com/2011/11/15/sql-server-introduction-to-lead-and-lag-analytic-functions-introduced-in-sql-server-2012/

Confused on count(*) and self joins

I want to return all application dates for the current month and for the current year. This must be simple, however I can not figure it out. I know I have 2 dates for the current month and 90 dates for the current year. Right, Left, Outer, Inner I have tried them all, just throwing code at the wall trying to see what will stick and none of it works. I either get 2 for both columns or 180 for both columns. Here is my latest select statement.
SELECT count(a.evdtApplication) AS monthApplicationEntered,
count (b.evdtApplication) AS yearApplicationEntered
FROM tblEventDates a
RIGHT OUTER JOIN tblEventDates b ON a.LOANid = b.loanid
WHERE datediff(mm,a.evdtApplication,getdate()) = 0
AND datediff(yy,a.evdtApplication, getdate()) = 0
AND datediff(yy,b.evdtApplication,getdate()) = 0
You don't need any joins at all.
You want to count the loanID column from tblEventDates, and you want to do it conditionally based on the date matching the current month or the current year.
SO:
SELECT SUM( CASE WHEN Month(a.evdtApplication) = MONTH(GEtDate() THEN 1 END) as monthTotal,
count(*)
FROM tblEventDates a
WHERE a.evdtApplication BETWEEN '2008-01-01' AND '2008-12-31'
What that does is select all the event dates this year, and add up the ones which match your conditions. If it doesn't match the current month it won't add 1. Actually, don't even need to do a condition for the year because you're just querying everything for that year.