SQL Comparing values by date range in two tables - sql

I have two tables in Sql Server 2008 -> SchedulePermanent and ScheduleImported
SchedulePermanent:
Id StartDate EndDate Value
1 01-01-2013 03-01-2013 100
2 03-01-2013 07-01-2013 200
3 07-01-2013 18-01-2013 300
CheduleImported:
Id StartDate EndDate Value
1 01-01-2013 04-01-2013 100
2 04-01-2013 06-01-2013 200
3 06-01-2013 15-01-2013 300
4 15-01-2013 18-01-2013 100
I want to insert into a result table only values that are not equal in the two schedules for each day.
Examlple: (OldValue: present in the permanent table, NewValue present in the import table)
Date OldValue NewValue
03-01-2013 200 100
06-01-2013 200 300
15-01-2013 300 100
16-01-2013 300 100
17-01-2013 300 100
18-01-2013 300 100
Do i have to split each table in temporary tables by date and then do the comparison for each date or is there a better way? (I saw some topics talking about Cross join but i have never used it)
Thank you,

I don't think you need a cross join, you should be able to do this with just an inner join.
select Date, SchedulePermanent.Value as OldValue, ScheduleImported.AsNewValue
from SchedulePermanent
join ScheduleImported on SchedulePermanent.StartDate = ScheduleImported.StartDate and SchedulePermanent.Value <> ScheduleImported.Value
However, that will only work to import values for dates that already exist in SchedulePermanent. To include values from imported that don't yet exist in permanent, it would look something like this:
select Date, SchedulePermanent.Value as OldValue, ScheduleImported.AsNewValue
from SchedulePermanent
right join ScheduleImported on SchedulePermanent.StartDate = ScheduleImported.StartDate
where SchedulePermanent.StartDate is null
If you want to overwrite the values, then you could check out a merge statement, instead.
MERGE SchedulePermanent AS target
USING (SELECT StartDate, Value from ScheduleImported) AS source
ON (target.StartDate = source.StartDate)
WHEN MATCHED THEN
UPDATE SET Value = source.Value
WHEN NOT MATCHED THEN
INSERT (StartDate, Value)
VALUES (source.StartDate, source.Value)

a simple way for you to get all the unequal records:
SELECT S.ID, S.StartDate, S.Value
FROM SchedulePermanent AS S
WHERE S.Value NOT EXSIST(
SELECT VALUE
FROM CheduleImported AS C
WHERE C.StartDate = S.StartDate AND C.EndDate = S.EndDate
)

Related

Extract non-existent values based on previous months?

I HAVE tb1
code Name sal_month
==== ===== ========
101 john 02/2017
102 mathe 02/2017
103 yara 02/2017
104 sara 02/2017
101 john 03/2017
102 mathe 03/2017
103 yara 03/2017
104 sara 03/2017
101 john 04/2017
103 yara 04/2017
In February all of them received salaries as well as March
How do I extract non-existent values based on previous months?
the result should be come
code sal_month
==== =======
102 04/2017
104 04/2017
Thank in advance
First I created this table:
create table #T(code int, sal_month varchar(10))
insert into #T values(101,'2/2017'),(102,'2/2017'),(103,'2/2017'),(104,'2/2017'),
(101,'3/2017'),(102,'3/2017'),(104,'3/2017'),(101,'4/2017'),(103,'4/2017')
Second, I executed this query:
SELECT code, Max(sal_Month)
From #T
Where code not in (select code from #T where sal_Month = (select Max(sal_Month) from #T))
Group by code
Then I got the following results:
Note: I am using SQL SERVER 2012
I think you can count salary_month grouped by id, something like this, and select the rows that shows less than 3 times.
select code, count (sal_month) from tb1
group by code
having count (sal_month) < 3
After that you join with initial table (just to filter the full rows which you need) on code.
So the final query will look like his:
select code, sal_month
from tb1 a
join (select code, count (sal_month) from tb1
group by code
having code < 3) X on a.code = X.code
Something like this:
DECLARE #DataSource TABLE
(
[code] INT
,[sal_month] VARCHAR(12)
);
INSERT #DataSource ([code], [sal_month])
VALUES (101, '2/2017')
,(102, '2/2017')
,(103, '2/2017')
,(104, '2/2017')
,(101, '3/2017')
,(102, '3/2017')
,(104, '3/2017')
,(101, '4/2017')
,(103, '4/2017');
WITH DataSource AS
(
SELECT *
,DENSE_RANK() OVER (ORDER BY [sal_month]) AS [MonthID]
,MAX([sal_month]) OVER () AS [MaxMonth]
FROM #DataSource DS1
)
SELECT DS1.[code]
,DS1.[sal_month]
FROM DataSource DS1
LEFT JOIN DataSource DS2
ON DS1.[code] = DS2.[code]
AND DS1.[MonthID] = DS2.[MonthID] - 1
LEFT JOIN DataSource DS3
ON DS1.[code] = DS3.[code]
AND DS1.[MonthID] = DS3.[MonthID] + 1
WHERE DS2.[code] IS NULL
AND DS3.[code] IS NOT NULL
AND DS1.[sal_month] <> DS1.[MaxMonth];
Some notes:
we need a way to sort the months and it is not easy as you are storing them in very unpractical way; you are not using a date/datetime column and your string is not a valid date; also, the string you are using is not good at all, because if you have [sal_month] from different years, we will not be able to sort them; you should think about this - one alternative is to use this format:
201512
201701
201702
201711
In this way we can sort by string.
in the core I am using ROW_NUMBER and sorting months as strings;
the idea is to look for all records, that have not exists in the next month, but have a record in the previous; at the same time, excluded records which are from the last month, as it's not possible for them to have record in the next month;
Try this:
select tb2.code, tb2.sal_month from tb
right join (
select code, sal_month, datepart(month, sal_month) + 1 as next_sal_month from tb) as tb2
on (tb.code = tb2.code and datepart(month, tb.sal_month) = tb2.next_sal_month)
where tb2.next_sal_month < 5 and tb.sal_month is null
In the result set there's one additional record: code 103 didn't receive salary in March, but did so in February, so it is included as well.
Here's SQL fiddle, to try :)
In the absence of more facts about your tables, create a cartesian product of the 2 axes of month & code, then left join the stored data. Then it is easy to identify missing items when no stored data exists when compared to every possible combination.
You might already have master tables of sal_month and/or code to use, if you do use those, but if not you can dynamically create them using select distinct as seen below.
create table tbl1 (code int, sal_month varchar(10))
insert into tbl1 values(101,'2/2017'),(102,'2/2017'),(103,'2/2017'),(104,'2/2017'),
(101,'3/2017'),(102,'3/2017'),(104,'3/2017'),(101,'4/2017'),(103,'4/2017')
select c.code, m.sal_month
from ( select distinct sal_month from tbl1 ) m
cross join ( select distinct code from tbl1 ) c
Left join tbl1 t on m.sal_month = t.sal_month and c.code = t.code
Where t.sal_month IS NULL
code | sal_month
---: | :--------
103 | 3/2017
102 | 4/2017
104 | 4/2017
dbfiddle here

Compare Multiple Values SQL

I am creating the 2 temporary tables below. I need to create a flag that says whether all of the weekly_sales values are less than the single average in Table 1, for each customer. What is the best way of doing this?
As an example, here is table 1:
Table 1
cust_nbr avg_sales
1234 200
And here is table 2:
Table 2
cust_nbr weekly_sales week
1234 222 1
1234 211 2
1234 121 4
Try this: it should select each customer, and flag them if their maximum weekly sales figure is still below the average set for them in Table1.
SELECT
A.Cust_nbr,
A.Avg_Sales,
CASE WHEN B.MaxSale < A.Avg_Sales THEN 1 ELSE 0 END IsAlwaysBelowAverage
FROM
Table1 A
LEFT JOIN
(
SELECT
Cust_Nbr,
MAX(Weekly_Sales) AS MaxSale
FROM Table2
) B ON
A.Cust_Nbr = B.Cust_Nbr
To get whether any single value is greater than all of the weekly_sales values, you can do something like this.
CASE
WHEN #avg > (
SELECT MAX(weeky_sales)
FROM [Table 2]
) THEN
1
ELSE
0
END
If you incorporate that into a function, you can add a computed column on [Table 1] to call the function based on the avg_sales.

SQL Server 2008 - need help on a antithetical query

I want to find out meter reading for given transaction day. In some cases there won’t be any meter reading and would like to see a meter reading for previous day.
Sample data set follows. I am using SQL Server 2008
declare #meter table (UnitID int, reading_Date date,reading int)
declare #Transactions table (Transactions_ID int,UnitID int,Transactions_date date)
insert into #meter (UnitID,reading_Date,reading ) values
(1,'1/1/2014',1000),
(1,'2/1/2014',1010),
(1,'3/1/2014',1020),
(2,'1/1/2014',1001),
(3,'1/1/2014',1002);
insert into #Transactions(Transactions_ID,UnitID,Transactions_date) values
(1,1,'1/1/2014'),
(2,1,'2/1/2014'),
(3,1,'3/1/2014'),
(4,1,'4/1/2014'),
(5,2,'1/1/2014'),
(6,2,'3/1/2014'),
(7,3,'4/1/2014');
select * from #meter;
select * from #Transactions;
I expect to get following output
Transactions
Transactions_ID UnitID Transactions_date reading
1 1 1/1/2014 1000
2 1 2/1/2014 1010
3 1 3/1/2014 1020
4 1 4/1/2014 1020
5 2 1/1/2014 1001
6 2 3/1/2014 1001
7 3 4/1/2014 1002
Your SQL Query to get your desired out put will as following:
SELECT Transactions_ID, T.UnitID, Transactions_date
, (CASE WHEN ISNULL(M.reading,'') = '' THEN
(
SELECT MAX(Reading) FROM #meter AS A
JOIN #Transactions AS B ON A.UnitID=B.UnitID AND A.UnitID=T.UnitID
)
ELSE M.reading END) AS Reading
FROM #meter AS M
RIGHT OUTER JOIN #Transactions AS T ON T.UnitID=M.UnitID
AND T.Transactions_date=M.reading_Date
I can think of two ways to approach this - neither of them are ideal.
The first (and slightly better) way would be to create a SQL Function that took the Transactions_date as a parameter and returned the reading for Max(Reading_date) where reading_date <= transactions_date. You could then use this function in a select statement against the Transactions table.
The other approach would be to use a cursor to iterate through the transactions table and use the same logic as above where you return the reading for Max(Reading_date) where reading_date <= transactions_date.
Try the below query:
Please find the result of the same in SQLFiddle
select a.Transactions_ID, a.UnitID, a.Transactions_date,
case when b.reading IS NULL then c.rd else b.reading end as reading
from
Transactions a
left outer join
meter b
on a.UnitID = b.UnitID
and a.Transactions_date = b.reading_Date
inner join
(
select UnitID,max(reading) as rd
from meter
group by UnitID
) as C
on a.UnitID = c.UnitID

Piviot data in two tables?

I have two tables as follows:
Table 1
Columns - oppproductid, SKU, Price, Quantity, Date
Values - PR1, ABCSKU1, 1000,500, 10/2013
Table 2
Columns - opproductid, month_1, Month_2, Month_3, Month_4...Month_36
Values - PR1, 200, 100, NULL, 200...
The tables are 1-1. I need to get one row for each value in the month column that is not null for each record and calculate the date based on the months that are not null assuming that Month_1 is the date column in the primary table so the ideal result set based on the sample values is:
oppproductid SKU Price Quantity Date Deployment
PR1 ABCSKU1 1000 500 10/2013 200
PR1 ABCSKU1 1000 500 11/2013 100
PR1 ABCSKU1 1000 500 1/2014 200
NOTES:
Month_3 is NULL so 12/2013 does not yield results.
There are 36 months in the second table with the only requirement that one has to contain data.
Month_1 always equals the date on the first table.
Any help is appreciated.
Store your data using the proper data types. Dates should be date fields.
Normalise your data structures to make querying easier.
Try this
.
set dateformat dmy
select
t1.oppproductid,
t1.SKU,
t1.Price,
t1.Quantity,
dateadd(month, monthno-1, convert(date, '1/' + [date])),
deployment
from table1 t1
inner join
(
select *, convert(int,substring(mth,7,2)) as monthno from table2
unpivot (deployment for mth in (month_1,month_2,month_3,month_4...)) u
) u2
on t1.oppproductid = u2.opproductid

DB2 SQL filter query result by evaluating an ID which has two types of entries

After many attempts I have failed at this and hoping someone can help. The query returns every entry a user makes when items are made in the factory against and order number. For example
Order Number Entry type Quantity
3000 1 1000
3000 1 500
3000 2 300
3000 2 100
4000 2 1000
5000 1 1000
What I want to the query do is to return filter the results like this
If the order number has an entry type 1 and 2 return the row which is type 1 only
otherwise just return row whatever the type is for that order number.
So the above would end up:
Order Number Entry type Quantity
3000 1 1000
3000 1 500
4000 2 1000
5000 1 1000
Currently my query (DB2, in very basic terms looks like this ) and was correct until a change request came through!
Select * from bookings where type=1 or type=2
thanks!
select * from bookings
left outer join (
select order_number,
max(case when type=1 then 1 else 0 end) +
max(case when type=2 then 1 else 0 end) as type_1_and_2
from bookings
group by order_number
) has_1_and_2 on
type_1_and_2 = 2
has_1_and_2.order_number = bookings.order_number
where
bookings.type = 1 or
has_1_and_2.order_number is null
Find all the orders that have both type 1 and type 2, and then join it.
If the row matched the join, only return it if it is type 1
If the row did not match the join (has_type_2.order_number is null) return it no matter what the type is.
A "common table expression" [CTE] can often simplify your logic. You can think of it as a way to break a complex problem into conceptual steps. In the example below, you can think of g as the name of the result set of the CTE, which will then be joined to
WITH g as
( SELECT order_number, min(type) as low_type
FROM bookings
GROUP BY order_number
)
SELECT b.*
FROM g
JOIN bookings b ON g.order_number = b.order_number
AND g.low_type = b.type
The JOIN ON conditions will work so that if both types are present then low_type will be 1, and only that type of record will be chosen. If there is only one type it will be identical to low_type.
This should work fine as long as 1 and 2 are the only types allowed in the bookings table. If not then you can simply add a WHERE clause in the CTE and in the outer SELECT.