SQL to display value for different dates - sql

I have a table named Reading_Hist containing columns such as Reading, Date, ID. This table contains history of the readings. example
+----+---------+-------------+
| ID | Reading | ReadingDate |
+----+---------+-------------+
| 1 | 12 | 9/12/2018 |
| 2 | 15 | 9/12/2018 |
| 1 | 16 | 9/5/2018 |
| 4 | 1 | 9/12/2018 |
| 3 | 65 | 9/12/2018 |
| 1 | 23 | 8/29/2018 |
| 3 | 25 | 9/5/2018 |
| 2 | 23 | 9/5/2018 |
| 4 | 3 | 9/5/2018 |
+----+---------+-------------+
I want to write a sql to display each ID and it's current Reading on first column, next reading taken a week before and third reading taken two weeks before and last trend of the reading.
Example Result below.
+----+---------+------+------+-------+
| ID | Current | Wk_1 | Wk_2 | Trend |
+----+---------+------+------+-------+
| 1 | 12 | 16 | 23 | Down |
| 2 | 15 | 23 | NULL | Down |
| 3 | 65 | 25 | NULL | UP |
| 4 | 1 | 3 | NULL | Down |
+----+---------+------+------+-------+

You can use aggregation to get the maximum day of readings per ID. Then left join the current readings, them of the last week and two weeks ago. Use CASE to calculate the trend.
It could look something like:
SELECT x.id,
rh2.reading current,
rh3.reading wk_1,
rh4.reading wk_2,
CASE
WHEN rh2.reading > rh3.reading THEN
'Up'
WHEN rh2.reading < rh3.reading THEN
'Down'
WHEN rh2.reading = rh3.reading THEN
'-'
END trend
FROM (SELECT rh1.id,
max(rh1.reading_date) reading_date
FROM reading_hist rh1
GROUP BY rh1.id) x
LEFT JOIN reading_hist rh2
ON rh2.id = x.id
AND rh2.reading_date = x.reading_date
LEFT JOIN reading_hist rh3
ON rh3.id = x.id
AND rh3.reading_date = dateadd(day, -7, x.reading_date)
LEFT JOIN reading_hist rh4
ON rh4.id = x.id
AND rh4.reading_date = dateadd(day, -14, x.reading_date);
Of course this requires, that there are readings exactly 7 or 14 days from the last day of readings.

Related

SQL some selections into one (or get two colums from one)

I use PostgreSql, I have two tables (for example)
Let table1 will contain stores, there are 2 types 'candy store' and 'dental store'.
Each row contains information about a customer's purchase in a particular store
In result i want to get money from each type of store group by id and the last date of purchase. Money from candy stores start sum since 2016, but money from dental stores start sum from 2018
table1:
+----+---------+------------------+-------+
| id | store | date of purchase | money |
| 1 | store 1 | 2016-01-01 | 10 |
| 1 | store 5 | 2018-01-01 | 50 |
| 2 | store 2 | 2017-01-20 | 10 |
| 2 | store 3 | 2019-02-20 | 15 |
| 3 | store 2 | 2017-02-02 | 20 |
| 3 | store 6 | 2019-01-01 | 60 |
| 1 | store 1 | 2015-01-01 | 20 |
+----+---------+------------------+-------+
table2 :
+---------+--------+
| store | type |
| store 1 | candy |
| store 2 | candy |
| store 3 | candy |
| store 4 | dental |
| store 5 | dental |
| store 6 | dental |
+---------+--------+
I want my query to return a table like this:
+----+---------------+-----------------+---------------+-----------------+
| id | money( candy) | the last date c | money(dental) | the last date d |
| 1 | 10 | 2016-01-01 | 50 | 2018-01-01 |
| 2 | 25 | 2019-02-20 | - | - |
| 3 | 20 | 2017-02-02 | 60 | 2019-01-01 |
+----+---------------+-----------------+---------------+-----------------+
if I understand correctly , this is what you want to do :
select id
, sum(money) filter (where ty.type = 'candy') candymoney
, max(purchasedate) filter (where ty.type = 'candy') candylastdate
, sum(money) filter (where ty.type = 'dental') dentalmoney
, max(purchasedate) filter (where ty.type = 'dental') dentallastdate
from table t
join storetype table st on t.store = ty.store
group by id

Query Different Condition With 1 Column

I have table like :
+-------+--------+----------+------------+-------+
| cd_hs | cd_cnt | name_cnt | dates | value |
+-------+--------+----------+------------+-------+
| 1 | 1 | aaa | 2018-06-01 | 50 |
| 1 | 2 | bbb | 2018-07-01 | 150 |
| 1 | 3 | ccc | 2018-08-01 | 20 |
| 1 | 1 | aaa | 2018-06-02 | 40 |
| 1 | 2 | bbb | 2018-07-02 | 70 |
| 1 | 3 | ccc | 2018-08-02 | 80 |
+-------+--------+----------+------------+-------+
Actually I have more data but I am just show the sample and what I want to do is
I want to group by cd_hs, name_cnt and based on year in dates column and do sum(value) but I have the 2 condition. First is to show value with condition cd_cnt with 1 and 2 and second condition cd_cnt without 1 and 2 so meaning I have much value other than 1 and 2 and do aliasing as other in one column
Expected Result :
+-------+------+----------+-------------+
| cd_hs | year | name_cnt | total_value |
+-------+------+----------+-------------+
| 1 | 2018 | aaa | 90 |
| 1 | 2018 | bbb | 220 |
| 1 | 2018 | other | 100 |
+-------+------+----------+-------------+
how can I do that? I am new in query and don't know what to do..
Your question is a bit confusing considering your spec doesn't seem to exactly line up with what you requested.
If the sample result you've provided is actually what you're looking for, a simple SUM and GROUP BY should do the trick here:
SELECT cd_hs, EXTRACT(YEAR from dates) as year, name_cnt, SUM(value_)
FROM foo
GROUP BY cd_hs, EXTRACT(YEAR from dates), name_cnt
Result:
| cd_hs | year | name_cnt | sum |
|-------|------|----------|-----|
| 1 | 2018 | aaa | 90 |
| 1 | 2018 | bbb | 220 |
| 1 | 2018 | ccc | 100 |
SQLFiddle
Since you mentioned you wanted two different totals with two separate conditions, you could use JOIN in conjunction with some well-crafted subqueries:
SELECT a.cd_hs, EXTRACT(YEAR FROM a.dates), a.name_cnt, COALESCE(b.total_a, 0) as "Total A", COALESCE(c.total_b, 0) as "Total B"
FROM foo a
LEFT JOIN (
SELECT b.cd_hs, b.name_cnt, EXTRACT(YEAR FROM b.dates), SUM(value_) as total_a
FROM foo b
WHERE b.cd_cnt NOT IN (1, 2)
GROUP BY b.cd_hs, b.name_cnt, EXTRACT(YEAR from b.dates)
) b ON a.cd_hs = b.cd_hs AND a.name_cnt = b.name_cnt
LEFT JOIN (
SELECT c.cd_hs, c.name_cnt, EXTRACT(YEAR FROM c.dates), SUM(value_) as total_b
FROM foo c
WHERE c.cd_cnt IN (1, 2)
GROUP BY c.cd_hs, c.name_cnt, EXTRACT(YEAR from c.dates)
) c ON a.cd_hs = c.cd_hs AND a.name_cnt = c.name_cnt
This particular solution is readable and will get you to the correct end result but will most likely not be scalable in its current form.
Result:
| cd_hs | date_part | name_cnt | Total A | Total B |
|-------|-----------|----------|---------|---------|
| 1 | 2018 | aaa | 0 | 90 |
| 1 | 2018 | bbb | 0 | 220 |
| 1 | 2018 | ccc | 100 | 0 |
| 1 | 2018 | aaa | 0 | 90 |
| 1 | 2018 | bbb | 0 | 220 |
| 1 | 2018 | ccc | 100 | 0 |
SQLFiddle

Calculate Status Chages and time in between status

I have a table as shown below. I have column that tracks an application and application status changes over time. I track the time it changes the status in the date column. The table is sorted by application and date of status change from oldest to newest.
+--------+-----------+--------+------------------+
| app_id | status_id | row_no | date |
+--------+-----------+--------+------------------+
| 1 | a | 10 | 2016-10-04 21:35 |
| 1 | b | 11 | 2016-10-12 21:50 |
| 1 | c | 12 | 2016-10-25 20:40 |
| 1 | d | 13 | 2016-10-26 16:10 |
| 1 | e | 14 | 2016-10-26 16:10 |
| 2 | a | 20 | 2016-09-15 1:26 |
| 2 | c | 21 | 2016-09-15 21:32 |
| 2 | d | 22 | 2016-09-16 21:51 |
| 2 | e | 23 | 2016-09-16 21:51 |
| 2 | f | 24 | 2016-09-20 22:55 |
| 2 | g | 25 | 2016-10-20 22:46 |
| 2 | g | 26 | 2016-10-20 22:46 |
+--------+-----------+--------+------------------+
I am trying to achieve how much time an application is spending before it reached the final state. Below is a sample of the table That i am trying to build in Sql. For every status i am trying to capture the next status. The previous status column shows the status in that row, while next status shows the next status in the next row for that application. If the application is at its last status then the next status is marked as Last. Next I calculate the time between status by calculating the hours differences between two dates. I would really appreciate if you can tell me how to achieve this functionality in Sql. Thank you in advance.
+--------+-----------+--------+------------------+-----------------+-------------+--------------+
| app_id | status_id | row_no | date | previous status | next status | time between |
+--------+-----------+--------+------------------+-----------------+-------------+--------------+
| 1 | a | 10 | 2016-10-04 21:35 | a | b | 192.2333333 |
| 1 | b | 11 | 2016-10-12 21:50 | b | c | 334.8333333 |
| 1 | c | 12 | 2016-10-25 20:40 | c | d | 43.48333333 |
| 1 | d | 13 | 2016-10-26 16:10 | d | e | 0 |
| 1 | e | 14 | 2016-10-26 16:10 | e | Last | Last |
| 2 | a | 20 | 2016-09-15 1:26 | a | c | 20.08333333 |
| 2 | c | 21 | 2016-09-15 21:32 | c | d | 24.31666667 |
| 2 | d | 22 | 2016-09-16 21:51 | d | e | 0 |
| 2 | e | 23 | 2016-09-16 21:51 | e | f | 97.06666667 |
| 2 | f | 24 | 2016-09-20 22:55 | f | g | 743.8333333 |
| 2 | g | 25 | 2016-10-20 22:46 | g | g | 0 |
| 2 | g | 26 | 2016-10-20 22:46 | g | Last | Last |
+--------+-----------+--------+------------------+-----------------+-------------+--------------+
It's a bit messy, but if you have a known amount of status_id's, you can try making each one in a "with" clause and joining them all together at the end on app_id. And then making a final table which calculates the steps between A and B, between B and C, etc. This wouldn't produce a table like the one you made, however. But it should get all the time differences.
with A_table as (
select
app_id,
date A_status
where status_id = 'a'
)
, B_table (
select
app_id,
date B_status
where status_id = 'b'
)
--MORE STATUS TABLE HERE
, combined_table (
select
a.app_id,
a.A_status,
b.B_status,
--MORE STATUS DATES HERE
from A_table a
left outer join B_table b on a.app_id = b.app_id
--LEFT OUTER JOIN MORE STATUS TABLES ON A_TABLE HERE
--YOU'RE MAKING ONE TABLE WITH EACH APP_ID ON ONE ROW WITH ALL TIME STAMPS
)
select
*,
B_status - A_status A_B
--MORE TIME SUBTRACTIONS HERE
--SINCE YOU'VE OUTER JOINED ABOVE, YOU'LL HAVE COLUMNS FOR ALL POSSIBLE
--STATUS STEPS AND THOSE WHICH DIDN'T HAVE THAT STEP WILL BE NULL
from combined
It's kind of clunky but with a fixed amount of status steps, should get the job done. It doesn't account for which step is the "last step" though. I don't know how important that is. You can always write a case statement that looks at the next step to see if it was null. What you want might be able to be achieved with loops but I've never used those.
Also note that if you have duplicate rows of app_id, status_id and date, like the last two rows in your sample table, you'd need to sort that out in the with tables some how, like only taking the first row, or ranking them.
Using Sql Lead and Lag function we can achive this. Here is the query:
select app_id, status as prev_status, date as prev_date, lead(status) over (partition by app_id order by date) as next_status, lead(date) over (partition by app_id order by date) as next_date from table

SQLiteStudio: Obtaining the most recent price from a price catalog

I'm fairly new to SQL and am trying to get the price for a product transaction on a particular date my looking up the most recent price of that product prior to the transaction within a price catalog.
Specifically, I have the two following tables:
Transactions Catalog
----------------------------------------------------------------------------
ProductID | Design | Transaction_DT ProductID | Price | Effective_DT
1 | Plaid | 5/14/2016 1 | 20 | 4/22/2016
2 | Solid | 3/26/2016 1 | 10 | 5/2/2016
3 | PolkaDot | 4/12/2016 1 | 5 | 5/15/2016
4 | Solid | 4/24/2016 2 | 50 | 3/22/2016
5 | PolkaDot | 2/24/2016 2 | 25 | 4/1/2016
6 | PinStripe | 3/29/2016 2 | 10 | 4/2/2016
3 | 30 | 4/5/2016
3 | 25 | 4/9/2016
3 | 22 | 4/12/2016
4 | 12 | 3/15/2016
4 | 8 | 3/27/2016
4 | 6 | 4/25/2016
5 | 15 | 2/23/2016
5 | 11 | 2/25/2016
5 | 6 | 2/28/2016
6 | 26 | 2/2/2016
6 | 17 | 3/19/2016
6 | 13 | 5/16/2016
I have entered the following code:
SELECT Transactions.ProductID,
Catalog.Price,
Transactions.Transaction_DT,
Transactions.Design
FROM Transactions
LEFT JOIN
Catalog ON Transactions.ProductID = Catalog.ProductID AND
Catalog.Effective_DT = (
SELECT MAX(Effective_DT)
FROM Catalog
WHERE Effective_DT <= Transactions.Transactions DT
)
And obtained the following output:
ProductID | Price | Transaction_DT | Design
1 | Null | 5/14/2016 | Plaid
2 | 50 | 3/26/2016 | Solid
3 | 22 | 4/12/2016 | PolkaDot
4 | Null | 4/24/2016 | Solid
5 | 15 | 2/24/2016 | PolkaDot
6 | Null | 3/29/2016 | PinStripe
I would like to return the Price for products 1, 4, and 6 to be 10, 8, and 17 respectively (in addition to the correct prices which were properly output) instead of the Null values I'm getting. Any ideas on how I can obtain the proper results?
You forgot to filter the correlated query by the productID. You are not getting the correct latest date for the product. You need to use this query:
SELECT Transactions.ProductID,
Catalog.Price,
Transactions.Transaction_DT,
Transactions.Design
FROM Transactions
LEFT JOIN
Catalog ON Transactions.ProductID = Catalog.ProductID AND
Catalog.Effective_DT = (
SELECT MAX(Effective_DT)
FROM Catalog
WHERE Effective_DT <= Transactions.Transactions_DT
and ProductID = Transactions.ProductID
)

How to calculate running total (month to date) in SQL Server 2008

I'm trying to calculate a month-to-date total using SQL Server 2008.
I'm trying to generate a month-to-date count at the level of activities and representatives. Here are the results I want to generate:
| REPRESENTATIVE_ID | MONTH | WEEK | TOTAL_WEEK_ACTIVITY_COUNT | MONTH_TO_DATE_ACTIVITIES_COUNT |
|-------------------|-------|------|---------------------------|--------------------------------|
| 40 | 7 | 7/08 | 1 | 1 |
| 40 | 8 | 8/09 | 1 | 1 |
| 40 | 8 | 8/10 | 1 | 2 |
| 41 | 7 | 7/08 | 2 | 2 |
| 41 | 8 | 8/08 | 4 | 4 |
| 41 | 8 | 8/09 | 3 | 7 |
| 41 | 8 | 8/10 | 1 | 8 |
From the following tables:
ACTIVITIES_FACT table
+-------------------+------+-----------+
| Representative_ID | Date | Activity |
+-------------------+------+-----------+
| 41 | 8/03 | Call |
| 41 | 8/04 | Call |
| 41 | 8/05 | Call |
+-------------------+------+-----------+
LU_TIME table
+-------+-----------------+--------+
| Month | Date | Week |
+-------+-----------------+--------+
| 8 | 8/01 | 8/08 |
| 8 | 8/02 | 8/08 |
| 8 | 8/03 | 8/08 |
| 8 | 8/04 | 8/08 |
| 8 | 8/05 | 8/08 |
+-------+-----------------+--------+
I'm not sure how to do this: I keep running into problems with multiple-counting or aggregations not being allowed in subqueries.
A running total is the summation of a sequence of numbers which is
updated each time a new number is added to the sequence, simply by
adding the value of the new number to the running total.
I THINK He wants a running total for Month by each Representative_Id, so a simple group by week isn't enough. He probably wants his Month_To_Date_Activities_Count to be updated at the end of every week.
This query gives a running total (month to end-of-week date) ordered by Representative_Id, Week
SELECT a.Representative_ID, l.month, l.Week, Count(*) AS Total_Week_Activity_Count
,(SELECT count(*)
FROM ACTIVITIES_FACT a2
INNER JOIN LU_TIME l2 ON a2.Date = l2.Date
AND a.Representative_ID = a2.Representative_ID
WHERE l2.week <= l.week
AND l2.month = l.month) Month_To_Date_Activities_Count
FROM ACTIVITIES_FACT a
INNER JOIN LU_TIME l ON a.Date = l.Date
GROUP BY a.Representative_ID, l.Week, l.month
ORDER BY a.Representative_ID, l.Week
| REPRESENTATIVE_ID | MONTH | WEEK | TOTAL_WEEK_ACTIVITY_COUNT | MONTH_TO_DATE_ACTIVITIES_COUNT |
|-------------------|-------|------|---------------------------|--------------------------------|
| 40 | 7 | 7/08 | 1 | 1 |
| 40 | 8 | 8/09 | 1 | 1 |
| 40 | 8 | 8/10 | 1 | 2 |
| 41 | 7 | 7/08 | 2 | 2 |
| 41 | 8 | 8/08 | 4 | 4 |
| 41 | 8 | 8/09 | 3 | 7 |
| 41 | 8 | 8/10 | 1 | 8 |
SQL Fiddle Sample
As I understand your question:
SELECT af.Representative_ID
, lt.Week
, COUNT(af.Activity) AS Qnt
FROM ACTIVITIES_FACT af
INNER JOIN LU_TIME lt ON lt.Date = af.date
GROUP BY af.Representative_ID, lt.Week
SqlFiddle
Representative_ID Week Month_To_Date_Activities_Count
41 2013-08-01 00:00:00.000 1
41 2013-08-08 00:00:00.000 3
USE tempdb;
GO
IF OBJECT_ID('#ACTIVITIES_FACT','U') IS NOT NULL DROP TABLE #ACTIVITIES_FACT;
CREATE TABLE #ACTIVITIES_FACT
(
Representative_ID INT NOT NULL
,Date DATETIME NULL
, Activity VARCHAR(500) NULL
)
IF OBJECT_ID('#LU_TIME','U') IS NOT NULL DROP TABLE #LU_TIME;
CREATE TABLE #LU_TIME
(
Month INT
,Date DATETIME
,Week DATETIME
)
INSERT INTO #ACTIVITIES_FACT(Representative_ID,Date,Activity)
VALUES
(41,'7/31/2013','Chat')
,(41,'8/03/2013','Call')
,(41,'8/04/2013','Call')
,(41,'8/05/2013','Call')
INSERT INTO #LU_TIME(Month,Date,Week)
VALUES
(8,'7/31/2013','8/01/2013')
,(8,'8/01/2013','8/08/2013')
,(8,'8/02/2013','8/08/2013')
,(8,'8/03/2013','8/08/2013')
,(8,'8/04/2013','8/08/2013')
,(8,'8/05/2013','8/08/2013')
--Begin Query
SELECT AF.Representative_ID
,LU.Week
,COUNT(*) AS Month_To_Date_Activities_Count
FROM #ACTIVITIES_FACT AS AF
INNER JOIN #LU_TIME AS LU
ON AF.Date = LU.Date
Group By AF.Representative_ID
,LU.Week