access sql changing where parameters - sql

I have a problem with a DB I'm working on, I suspect is actually a very simple problem, still I can't solve it.
For sake of brevity I will use a very simplified case.
Let's say you have a table were have been stored data about expenses made by different persons, something like this:
IDPerson Expense data
1 2.00 1/1/14
1 1.00 1/2/14
1 1.00 1/3/14
2 1.00 1/1/14
2 1.00 1/2/14
3 1.00 1/3/14
What i need is a query that gives me, for each IDPerson, the totale of expenses made before or on 1/2/14 AND the total of the expenses unregarding the date, so
IDPerson TotalExpense ExpenseBefore1/2/14
1 4.00 2.00
2 2.00 2.00
3 1.00 0.00
It seems to me that to get this you shuld have different where clauses for each column(hence the title)
What I tried:
I tried to use two queries, one without where clause and one with
Where data<=1/2/14
then I used UNION to get the final result, technically it worked, but since this it is not really handy expecially because the real project is much more complicated than this and the final table has much more fields; is there any other way to obtain this?
P.S. You may have noticed that English is not my mothertongue, sorry for any error

SELECT y.IDPerson,y.ExpensesTotal,isnull(x.ExpensesBefore_1_2_2014,0) as ExpensesBefore_1_2_2014 from
(SELECT IDPerson ,sum(expenses) as ExpensesTotal from tableA GROUP BY IDPerson) as y full join ( SELECT tableA.IDPerson , sum(expenses) as ExpensesBefore_1_2_2014 from tableA
where data < '01.02.2014'
GROUP BY tableA.IDPerson ) as x on x.IDPerson = y.IDPerson
This query should work, also it should be preaty quick if the IDPerson is Indexed or PK.
I've updated the query, try this one.
SQL Fiddle : http://sqlfiddle.com/#!3/fdaf0/1

Related

Duplicate rows because 1 column has multiple distinct values

I'm running a SELECT query to get data across multiple tables in the same server instance. However I've just noticed that the rows pulled on some data get duplicated because the main table I'm pulling from has a few different values in one of the columns. Here's the query:
SELECT DISTINCT BIF030.C_ACCOUNT AS ACCOUNTNUMBER,
BIF003.C_ACCOUNTTYPE AS ACCOUNTTYPECODE,
CON013.C_DESCRIPTION AS ACCOUNTTYPE,
BIF003.C_DIVISION AS ZONE_DIVISONCODE,
CON028.C_DESCRIPTION AS ZONE_DIVISION,
BIF030.C_METER as METERNUMBER,
BIF005.C_METERCUSTOM1 AS REGISTERNUMBER,
CONVERT(DECIMAL(20,2), BIF030.N_CONSUMP) AS CONSUMPTION,
CON007.C_DESCRIPTION AS UNITS,
BIF030.T_READDATE AS READINGDATE,
MONTH(BIF030.T_READDATE) AS READINGMONTH,
DAY(BIF030.T_READDATE) AS READINGDAY,
YEAR(BIF030.T_READDATE) AS READINGYEAR,
BIF030.I_DAYS AS READINGDAYSCOUNT
FROM ADVANCED.BIF030
LEFT JOIN ADVANCED.CON007 ON CON007.C_UNITS=BIF030.C_UNITS
LEFT JOIN ADVANCED.BIF005 ON BIF005.C_METER=BIF030.C_METER
LEFT JOIN ADVANCED.BIF003 ON BIF003.C_ACCOUNT=BIF030.C_ACCOUNT
LEFT JOIN ADVANCED.CON013 ON CON013.C_ACCOUNTTYPE=BIF003.C_ACCOUNTTYPE
LEFT JOIN ADVANCED.CON028 ON CON028.C_DIVISION=BIF003.C_DIVISION
WHERE T_READDATE > '01-01-2014'
ORDER BY ACCOUNTNUMBER, READINGDATE ASC
I know SELECT DISTINCT is frowned upon, but I get even more rows without it. Here's a sample of what the data looks like when pulled:
ACCOUNTNUMBER
ACCOUNTTYPECODE
ACCOUNTTYPE
ZONE_DIVISIONCODE
ZONE_DIVISION
METERNUMBER
REGISTERNUMBER
CONSUMPTION
UNITS
READINGDATE
READINGMONTH
READINGDAY
READINGYEAR
READINGDAYSCOUNT
1234567
SP
ACCOUNT TYPE 1
00
00-NO ZONE
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
1234567
MF
ACCOUNT TYPE 2
02
02-GRAVITY
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
1234567
SR
ACCOUNT TYPE 3
02
02-GRAVITY
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
I also know the column that is messing this up is the "AccountTypeCode" because other accounts that don't have multiple codes associated with the "AccountNumber" only show 1 set of rows. So this one specifically (and probably others) is tripling the amount of rows pulled when it should only pull one for each "ReadingDate".
Also if anyone knows a good way to optimize the query I'd be happy to learn. I know just enough SQL to be dangerous, but not enough to figure this out. Thanks.
Ok. So good news and I want to add this in case it helps anyone else in the future. I found out that since the ACCOUNTTYPECODE and ZONE_DIVISIONCODE were coming from the table BIF003 I needed to add more in the WHERE statement. This is what fixed it for me:
AND BIF030.C_CUSTOMER = BIF003.C_CUSTOMER
Because the C_CUSTOMER column was different (it's a column in the BIF003 and BIF030 tables) which lead to the separate ACCOUNTTYPECODE results I need to check it in the WHERE statement.
Thanks everyone for kick starting my brain on this one.

Combining Data from Two Tables into One Row

I've read around quite a bit for a solution to my problem but I can't seem to get it to work. It seems like a simple problem but I'm not getting the result set I want.
I'm working on a report that needs to pull from two tables and essentially create one row of data for each employee. The file needs to be uploaded to a healthcare vendor.
Here is an example of the data
Table1: EmployeeCheckDeduction
Employee ID Deduction Amount Check Date
1234 50.00 6/30/2015
1234 50.00 7/15/2015
4567 100.00 6/30/2015
4567 100.00 7/15/2015
9876 75.00 6/30/2015
9876 75.00 7/15/2015
Table2: EmployerContribution
Employee ID Contribution Amount Check Date
1234 25.00 6/30/2015
1234 30.00 7/15/2015
4567 50.00 6/30/2015
4567 60.00 7/15/2015
Part of the problem is that not every record in Table1 will have a corresponding match in Table 2. If they are maxed out on contributions, they won't receive one on that pay. What I want is a result set that looks like this:
Employee ID Deduction Amount Contribution Amount Check Date
1234 50.00 25.00 6/30/2015
1234 50.00 30.00 7/15/2015
4567 100.00 50.00 6/30/2015
4567 100.00 60.00 7/15/2015
9876 75.00 0.00 6/30/2015
9876 75.00 0.00 7/15/2015
No matter how I try and join, it's just duplicating data. I've tried using subqueries or distinct records and no matter what I try, it's not giving me what I want. I can't figure out what I'm doing wrong.
Edit. See links below for dataset results.
http://s000.tinyupload.com/index.php?file_id=01551050904538574848
http://s000.tinyupload.com/index.php?file_id=63978789937644749322
http://s000.tinyupload.com/index.php?file_id=28700836121558977952
I think part of the problem is that in the Employee Check Deduction table there is a specific deduction code that I'm pulling out. In the employer deduction table that code also exists. However, whenever I try and add the join on those 2 fields in addition to employee ID and check date, it doesn't return results from the employees who have a deduction amount in the employee check deduction table when they don't have a corresponding record in the Employer Contribution Table. I hope that helps.
select a.employee_id, a.deduction_amount, b.contribution_amount,
a.check_date
from employeecheckdeduction a join employercontribution b
on a.employee_id = b.employee_id
and a.check_date = b.check_date
Try this by renaming the columns if necessary. You should join on employed and checkdate.
SELECT a.EMPLID as EmployeeID, a.AL_AMOUNT as EmployeeContribution, ISNULL(b.AL_AMOUNT, 0) AS EmployerContribution, a.CHECK_DT
FROM PS_AL_CHK_DED as a
LEFT JOIN PS_AL_CHK_MEMO as b ON a.EMPLID = b.EMPLID AND a.CHECK_DT = b.CHECK_DT
WHERE a.AL_DEDCD = 'H'
ORDER BY a.CHECK_DT
The problem here is that you are dealing with TWO different things:
a) You want a FULL list of all employees who have ever had a contribution AND/OR a deduction
b) You then want to display it in one row
While there are many ways to go about this, the first thing would be two separate out those two and handle them.
Also it is not clear if you want to GROUP BY the employee ID or if you want to see a full history. If you are to look at history, the best thing would be using a FULL JOIN such as:
select coalesce(d.employee_id,c.employee_id) employee_id
, d.deduction_amount
, c.contribution_amount
, coalesce(d.deducation_date,c.contribution_date) check_date
from employee_deduction d
full join employee_contribution c on c.employee_id = d.employee_id
where d.DED_CD = 'H' or c.MEMO_CD = 'H'
What a FULL JOIN does is include ALL records from the first table and ALL records from the joining table regardless if there are nulls in either table.
That is why you have to use a coalesce statement in the select clause to make sure you don't wind up with a null value in the result set.
Hope this works.

MS SQL query - 2 tables same field - decide whitch to use

I have a simple question.
I have 2 tables - Items and Items_Discount with the same field (actually two fields but for same use) Price and Price_Discount.
ItemID Price PriceDiscount
1234 2.10 1.90
2346 15.60 NULL
7527 3.20 2.80
What I need is to have the PriceDiscount value when it is not NULL, and Price value when it is.
What to do ?
COALESCE would seem to do exactly what you need;
SELECT COALESCE(PriceDiscount, Price) AS Price FROM mytable
A simple SQLfiddle to test with.
Use
Select ItemID,IsNULL(PriceDiscout,Price) as Pricing
from <your tables>

Finding the next occurrence of a value in a table

Sorry in advance if this has already been covered.
I am working on a database which isnt particularly well structured but it is owned by a third party and cannot be changed.
I need some assistance with t-sql in find the next occurrence of a value within the table and return records based on the result. Let me first explain the data. I have simplified this to make it easier to understand.
Polref Effective Date Transaction Type Suffix Value
ABCD1 01/06/2010 New Bus 1 175.00
ABCD1 01/06/2011 Ren 2 200.00
ABCD1 19/08/2011 Adjust 3 50.00
ABCD1 23/04/2012 Adjust 4 50.00
ABCD1 01/06/2012 Ren 5 275.00
So if I ran my query for 2011, the code would need to return in this example rows with suffix 2,3 and 4. So what I have been trying to do is find the first suffix of a New Bus or Ren for the specified year and then finding the next suffix for a New Bus or Ren for the same polref and then using those two suffix values to limit my recordset. It aint working!!
I cant use MAX() as transactions for 2013 have already been added to the system to I would get more records than I actually need.
There result I should be expecting for this example data would be:
ABCD1 300.00
Any help would be greatly appreciated.
To answer another question, If I select 2011 as my year to run the report, there should only be one New Bus or Ren transaction for 2011 so if its a New Bus transaction, the next main transaction will be a Ren, if its a Ren then the next main transaction will be a Ren. Again in my example below, if I run for 2011, it should find the Ren from 01/06/2011 so I want to return that Ren and the two Adjust records.
Sorry, I've not used this forum before so apologies if I was a little vague.
The table I am using has many polrefs so I need this code to calculate totals for all polrefs that fall within the date range. Some polrefs may only have one row, a New Bus, some will have many rows depending on how many adjustments have been made throughout the year of the policy
Partial answer:
This query:
declare #t table (PolRef char(5) not null, EffectiveDate date not null,TransactionType varchar(10) not null,Suffix int not null,Value decimal(10,2) not null)
insert into #t (Polref,EffectiveDate,TransactionType,Suffix,Value) values
('ABCD1','20100601','New Bus',1,175.00),
('ABCD1','20110601','Ren',2,200.00),
('ABCD1','20110819','Adjust',3,50.00),
('ABCD1','20120423','Adjust',4,50.00),
('ABCD1','20120601','Ren',5,275.00)
;With StartTransactions as (
select PolRef,Suffix,ROW_NUMBER() OVER (PARTITION BY PolRef ORDER BY Suffix) rn
from #t where TransactionType in ('New Bus','Ren')
), Periods as (
select st1.PolRef,st1.Suffix as StartSuffix,st2.Suffix as EndSuffix
from
StartTransactions st1
left join
StartTransactions st2
on
st1.PolRef = st2.PolRef and
st1.rn = st2.rn - 1
)
select
p.PolRef,t2.EffectiveDate,SUM(t.Value) as Total
from
Periods p
inner join
#t t
on
p.PolRef = t.PolRef and
p.StartSuffix <= t.Suffix and
(p.EndSuffix > t.Suffix or
p.EndSuffix is null)
inner join
#t t2
on
p.PolRef = t2.PolRef and
t2.Suffix = p.StartSuffix
group by
p.PolRef,t2.EffectiveDate
Groups each set of transactions based on each successive Ren or New Bus transaction:
PolRef EffectiveDate Total
------ ------------- ---------------------------------------
ABCD1 2010-06-01 175.00
ABCD1 2011-06-01 300.00
ABCD1 2012-06-01 275.00
From that, it should be trivial to e.g. select out only the ones you're interested in from a particular year. But your question is still vague on some specifics, so I'm not taking it any further at this point.

Optimal solution for interview question

Recently in a job interview, I was given the following problem.
Say I have the following table
widget_Name | widget_Costs | In_Stock
---------------------------------------------------------
a | 15.00 | 1
b | 30.00 | 1
c | 20.00 | 1
d | 25.00 | 1
where widget_name is holds the name of the widget, widget_costs is the price of a widget, and in stock is a constant of 1.
Now for my business insurance I have a certain deductible. I am looking to find a sql statement that will tell me every widget and it's price exceeds the deductible. So if my dedudctible is $50.00 the above would just return
widget_Name | widget_Costs | In_Stock
---------------------------------------------------------
a | 15.00 | 1
d | 25.00 | 1
Since widgets b and c where used to meet the deductible
The closest I could get is the following
SELECT
*
FROM (
SELECT
widget_name,
widget_price
FROM interview.tbl_widgets
minus
SELECT widget_name,widget_price
FROM (
SELECT
widget_name,
widget_price,
50 - sum(widget_price) over (ORDER BY widget_price ROWS between unbounded preceding and current row) as running_total
FROM interview.tbl_widgets
)
where running_total >= 0
)
;
Which gives me
widget_Name | widget_Costs | In_Stock
---------------------------------------------------------
c | 20.00 | 1
d | 25.00 | 1
because it uses a and b to meet the majority of the deductible
I was hoping someone might be able to show me the correct answer
EDIT: I understood the interview question to be asking this. Given a table of widgets and their prices and given a dollar amount, substract as many of the widgets you can up to the dollar amount and return those widgets and their prices that remain
I'll put an answer up, just in case it's easier than it looks, but if the idea is just to return any widget that costs more than the deductible then you'd do something like this:
Select
Widget_Name, Widget_Cost, In_Stock
From
Widgets
Where
Widget_Cost > 50 -- SubSelect for variable deductibles?
For your sample data my query returns no rows.
I believe I understand your question, but I'm not 100%. Here is what I'm assuming you mean:
Your deductible is say, $50. To meet the deductible you have you "use" two items. (Is this always two? How high can it go? Can it be just one? What if they don't total exactly $50, there is a lot of missing information). You then want to return the widgets that aren't being used towards deductible. I have the following.
CREATE TABLE #test
(
widget_name char(1),
widget_cost money
)
INSERT INTO #test (widget_name, widget_cost)
SELECT 'a', 15.00 UNION ALL
SELECT 'b', 30.00 UNION ALL
SELECT 'c', 20.00 UNION ALL
SELECT 'd', 25.00
SELECT * FROM #test t1
WHERE t1.widget_name NOT IN (
SELECT t1.widget_name FROM #test t1
CROSS JOIN #test t2
WHERE t1.widget_cost + t2.widget_cost = 50 AND t1.widget_name != t2.widget_name)
Which returns
widget_name widget_cost
----------- ---------------------
a 15.00
d 25.00
This looks like a Bin Packing problem these are really hard to solve especially with SQL.
If you search on SO for Bin Packing + SQL, you'll find how to find Sum(field) in condition ie “select * from table where sum(field) < 150” Which is basically the same problem except you want to add a NOT IN to it.
I couldn't get the accepted answer by brianegge to work but what he wrote about it in general was interesting
..the problem you
describe of wanting the selection of
users which would most closely fit
into a given size, is a bin packing
problem. This is an NP-Hard problem,
and won't be easily solved with ANSI
SQL. However, the above seems to
return the right result, but in fact
it simply starts with the smallest
item, and continues to add items until
the bin is full.
A general, more effective bin packing
algorithm would is to start with the
largest item and continue to add
smaller ones as they fit. This
algorithm would select users 5 and 4.
So with this advice you could write a cursor to loop over the table to do just this (it just wouldn't be pretty).
Aaron Alton gives a nice link to a series of articles that attempts to solve the Bin Packing problem with sql but basically concludes that its probably best to use a cursor to do it.