SQL - Some result when no row - sql

I have a table (DATA), from which I would like to perform some simple math queries
Security Date Price
-------- ---- ------
1 2017-08-31 130
2 2017-08-31 150
1 2017-07-31 115
2 2017-07-31 140
1 2017-06-30 100
2 2017-06-30 130
1 2017-05-31 90
1 2017-04-30 85
1 2017-03-31 80
SELECT x.Security, x.Price/y.Price-1 'MONTHLY RETURN', x.Price/z.Price-1 'QUARTERLY RETURN'
FROM DATA AS x
JOIN DATA AS y
ON x.Security = y.Security
JOIN DATA AS z
ON x.Security = z.Security
WHERE x.Security IN (1,2)
AND x.Date = '2017-08-31'
AND y.Date = '2017-07-31'
AND z.Date = '2017-05-31'
I would like to have full table, even when there is no result (150/NO ROW)-1
Security MONTHLY RETURN QUARTERLY RETURN
-------- -------------- ----------------
1 (130/115)-1 (130/90)-1
2 (150/140)-1 NULL or any other data
Instead SQL is returning results just for Security 1 as there is no data (NO ROW) for Security 2 for '2017-05-31'. However it is not a good solution as I have data for '2017-07-31' so I would like to see them for Security 2.
Result of my query:
Security MONTHLY RETURN QUARTERLY RETURN
-------- -------------- ----------------
1 (130/150)-1 (130/90)-1
Is there any way to somehow prepare a table in which I will have all the data and let's say NULL when there is no result. Please note that normally I will have 20 or 30 securities.
I will be grateful for your assist on this one.
This is my second month with SQL and it is a way better than at the beginning but I still have some issues with logic. Do you know any good literature to start with?

Related

Apply a discount to order if user already ordered something else

I have a table with users, a table with levels, a table for submitted orders and processed orders.
Here's what the submitted orders looks like:
OrderId UserId Level_Name Discounted_Price Order_Date Price
1 1 OLE Core 0 2020-11-01 00:00:00.000 19.99
2 1 Xandadu 1 2020-11-01 00:00:00.000 0
3 2 Xandadu 0 2020-12-05 00:00:00.000 5
4 1 Eldorado 1 2021-01-31 00:00:00.000 9
5 2 Eldorado 0 2021-02-20 00:00:00.000 10
6 2 Birmingham Blues NULL 2021-07-10 00:00:00.000 NULL
What I am trying to do:
UserId 2 has an order for Birmingham Blues, they have already ordered Eldorado and so qualify for a discount on their Birmingham Blues order. Is there a way to check the entire table for this similarity, and if it exists update the discounted price to a 1 and change the price to lets say 10 for the Birmingham Blues order.
EDIT: I have researched the use of cursors, which I'm sure will do the job but they seem complicated and was hoping a simpler solution would be possible. A lot of threads seem to also avoid using cursors. I also looked at this question: T-SQL: Deleting all duplicate rows but keeping one and was thinking I could potentially use the answer to that in some way.
Based on your description and further comments, the following should hopefully meet your requirements - updating the row for the specified User where the values are currently NULL and the user has a qualifying existing order:
update s set
s.Discounted_Price = 1,
Price = 10
from submitted_Orders s
where s.userId=2
and s.Level_Name = 'Birmingham Blues'
and s.discounted_Price is null
and s.Price is null
and exists (
select * from submitted_orders so
where so.userId = s.userId
and so.Level_name = 'Eldorado'
and so.Order_Date < s.OrderDate
);

Write a query in MSSQL to get report for last 30 days

I want to create a report basis on,
Lead Name
Verified By
Verified on
Lead 1
ABC
11-02-2021
Lead 2
KMJ
9-02-2021
Lead 3
ABC
11-02-2021
The report will look like,
Consider today's date as 12-02-2021, we need to create a report for the last 30 days for employees work count
user
12-02-2021
11-02-2021
10-02-2021
9-02-2021
8-02-2021
7-02-2021
so on till last 30 days
ABC
0
2
0
0
0
0
XYZ
0
0
0
0
0
0
KMJ
0
0
0
1
0
0
I have written MSSQL Query as below,
CAST(lead.CREATED_ON as date) between cast(DATEADD(day, -30, getdate()) as date) and CAST(getdate() as date)
but, I am not able to get data in the below format, and also if there no entry for one date that date should show 0 in front of all users
user
12-02-2021
11-02-2021
10-02-2021
9-02-2021
8-02-2021
7-02-2021
so on
Kindly help me to complete this query, if possible kindly share any article link, it will be a great help for me thank you
First of all, are the dates really stored as strings like that? If so, that's a big problem that will make this already-difficult situation much worse. It's important enough you should consider the current schema as actively broken.
Moving on, the most correct way to handle this situation is pivot the data in the client code or reporting tool. That is, return a result set from the SQL database looking more like this:
User | Date | Value
ABC | 2021-02-12 | 0
ABC | 2021-02-12 | 2
ABC | 2021-02-10 | 0
ABC | 2021-02-09 | 0
ABC | 2021-02-08 | 0
ABC | 2021-02-07 | 0
XYZ | 2021-02-12 | 0
XYZ | 2021-02-12 | 0
XYZ | 2021-02-10 | 0
XYZ | 2021-02-09 | 0
XYZ | 2021-02-08 | 0
XYZ | 2021-02-07 | 0
... and so on
And then let the client do the work to reformat this data however you want, rather than asking a server that is often licensed at thousands of dollars per cpu core and isn't suited for the task to do that work.
But if you really must do this on the database server, you should know the SQL language has a very strict requirement to know the number, name, and type of columns in the output at query evaluation time, before looking at any data. Even SELECT * respects this, because the * is based on a table definition known ahead of time.
If the output won't know how many columns there are until it looks at the data, you must do this in 3 steps:
Run a query to get data about columns.
Use the result from step 1 to build a new query dynamically.
Run the new query.
However, since it looks like you always want exactly 30 days worth of data, you may be able to do this in a single query using the PIVOT keyword if you're willing to name the columns something more like OneDayBack, TwoDaysBack, ThreeDaysBack, ... ThirtyDaysBack, such that you can reference them in the pivot code regardless of the current date.

aggregation at different levels, I need to get sum by type level in sql server

ID TYPE AMT PERIOD
-------------------------------------------
1001 1 4500 2008-09-30
1001 2 2333 2008-09-30
1001 2 -2333 2008-09-30
1001 1 -200 2008-09-30
1001 2 300 2008-09-30
Desired output:
ID TYPE AMT PERIOD
------------------------------------
1001 1 4300 2008-09-30
1001 2 300 2008-09-30
I need to get sum of the AMT but still needs to include other columns, please see the desired output as shown above.
I tried using grouping sets but can't get it right.
SELECT ID,TYPE,SUM(AMT) as Total_Amt,PERIOD
FROM table
GROUP BY ID,TYPE,PERIOD

Running Totals Results Incorrect

I have a cursor that collects information from a different table and then updates the summary,here is the code snippet that is giving me a headache.
I am trying to update the summary table by input the correct Males and Females information that will add up to the total numbers of patients.
BEGIN
Set #MaleId =154
set #Femaleid =655
if #Category =#MaleId
begin
set #Males = #Value
end
if #Category = #Femaleid
begin
set #CummFemalesOnHaart =#Value
end
Update Stats
set Males =#Malest,Females =#Females
where
Category =#Category and
periodType =#PeriodType AND StartDate =#Startdate and EndDate =#enddate
Problem:
The results are inaccurate
organisation PeriodType StartDate EndDate Deaths Patients Males Females
------------ ---------- ---------- ---------- ------ -------- ----- -------
34 Monthly 2010-04-01 2010-04-30 NULL 6843 896 463
34 Monthly 2010-04-01 2010-04-30 NULL 10041 896 463
34 Monthly 2010-05-01 2010-05-31 NULL 10255 898 463
34 Monthly 2010-05-01 2010-05-31 NULL 7086 898 461
34 Monthly 2010-06-01 2010-06-30 NULL 10344 922 461
36 Monthly 2010-03-01 2010-03-31 NULL 4317 1054 470
36 Monthly 2010-03-01 2010-03-31 NULL 5756 896 470
36 Monthly 2010-04-01 2010-04-30 NULL 4308 896 463
36 Monthly 2010-04-01 2010-04-30 NULL 5783 896 463
The Males and Female should only update single rows that add up to the total number of patiens
e,g
Patients Males Females
-------- ----- -------
41 21 20
Any ideas?
Just to clarify a bit more
/Query Table/
organisation PeriodType StartDate EndDate Males Females
------------ ---------- ---------- ---------- ----- -------
34 Monthly 01-02-2012 01-02-2012 220 205
34 Monthly 01-02-2012 01-02-2012 30 105
/*Update statetement */
Update Stats
set Males =#Malest,Females =#Females
where
--Category =#Category and
periodType =#PeriodType AND StartDate =#Startdate and EndDate =#enddate
Stats Table /Before Input/
organisation PeriodType StartDate EndDate Deaths Patients Males Females
------------ ---------- ---------- ---------- ------ -------- ----- -------
34 Monthly 01-02-2012 01-02-2012 0 425 null null
34 Monthly 01-02-2012 01-02-2012 25 null null null
34 Monthly 01-02-2012 01-02-2012 5 null null null
34 Monthly 01-02-2012 01-02-2012 5 135 null null
/if you look at closely at the rows the period type,startdate,enddate are the same so I don't want the update values to affect any other row besides the ones with the Patients Totals/
Stats Table /* Output */
organisation PeriodType StartDate EndDate Deaths Patients Males Females
------------ ---------- ---------- ---------- ------ -------- ----- -------
34 Monthly 01-02-2012 01-02-2012 0 425 220 205
34 Monthly 01-02-2012 01-02-2012 25 null null null
34 Monthly 01-02-2012 01-02-2012 5 null null null
34 Monthly 01-02-2012 01-02-2012 5 135 30 105
I hope this gives more information
What you are proposing, from what I can see, will always run the risk of having incorrect data assigned. What happens when you have two records, one with patients 500 (males 250 and females 250) and another with patients 500 (but males 300 and females 200)?
There is no clear distinction which record totals to then use, and you'll end up with possible multiple updates with incorrect data.
That being said, I think what you should do is the following.
create a CTE which selects the patients from stats table, and males / females from query table, where patients = (males + females)
Run the update statement on the stats table, using values from the CTE joined on patients total.
This should, as far as I can understand, give a solution to what you're after.
You can read more about CTE's here: MSDN Common Table Expressions
I'm not entirely sure I understand your question... but think this is what you are after?
UPDATE Stats
SET Males = (SELECT COUNT(*) From <queryTable> WHERE Category = #MaleId and periodType =#PeriodType AND StartDate =#Startdate and EndDate =#enddate)
,SET Females = (SELECT COUNT(*) From <queryTable> WHERE Category = #FemaleId and periodType =#PeriodType AND StartDate =#Startdate and EndDate =#enddate)
Hope it helps!
I am not sure I understand the question either. But it sounds like you are saying there can be multiple records having the same periodType, startDate and endDate, but a different number of Patients. You wish to ensure the query only updates records where the total number of Patients matches the total you calculated ie total = #Males + #Females.
If that is correct, add a filter on the Patients value:
UPDATE Stats
SET Males = #Males,
Females = #Females
WHERE Category = #Category
AND periodType = #PeriodType
AND StartDate = #Startdate
AND EndDate = #enddate
AND Patients = (#Males + #Females)
Though as an aside, cursors generally tend to be less efficient than set based alternatives ..

Find two consecutive rows

I'm trying to write a query that will pull back the two most recent rows from the Bill table where the Estimated flag is true. The catch is that these need to be consecutive bills.
To put it shortly, I need to enter a row in another table if a Bill has been estimated for the last two bill cycles.
I'd like to do this without a cursor, if possible, since I am working with a sizable amount of data and this has to run fairly often.
Edit
There is an AUTOINCREMENT(1,1) column on the table. Without giving away too much of the table structure, the table is essentially of the structure:
CREATE TABLE Bills (
BillId INT AUTOINCREMENT(1,1,) PRIMARY KEY,
Estimated BIT NOT NULL,
InvoiceDate DATETIME NOT NULL
)
So you might have a set of results like:
BillId AccountId Estimated InvoiceDate
-------------------- -------------------- --------- -----------------------
1111196 1234567 1 2008-09-03 00:00:00.000
1111195 1234567 0 2008-08-06 00:00:00.000
1111194 1234567 0 2008-07-03 00:00:00.000
1111193 1234567 0 2008-06-04 00:00:00.000
1111192 1234567 1 2008-05-05 00:00:00.000
1111191 1234567 0 2008-04-04 00:00:00.000
1111190 1234567 1 2008-03-05 00:00:00.000
1111189 1234567 0 2008-02-05 00:00:00.000
1111188 1234567 1 2008-01-07 00:00:00.000
1111187 1234567 1 2007-12-04 00:00:00.000
1111186 1234567 0 2007-11-01 00:00:00.000
1111185 1234567 0 2007-10-01 00:00:00.000
1111184 1234567 1 2007-08-30 00:00:00.000
1111183 1234567 0 2007-08-01 00:00:00.000
1111182 1234567 1 2007-07-02 00:00:00.000
1111181 1234567 0 2007-06-01 00:00:00.000
1111180 1234567 1 2007-05-02 00:00:00.000
1111179 1234567 0 2007-03-30 00:00:00.000
1111178 1234567 1 2007-03-02 00:00:00.000
1111177 1234567 0 2007-02-01 00:00:00.000
1111176 1234567 1 2007-01-03 00:00:00.000
1111175 1234567 0 2006-11-29 00:00:00.000
In this case, only records 1111188 and 1111187 would be consecutive.
Assuming the rows have sequential IDs, something like this may be what you're looking for:
select top 1 *
from
Bills b1
inner join Bills b2 on b1.id = b2.id - 1
where
b1.IsEstimate = 1 and b2.IsEstimate = 1
order by
b1.BillDate desc
select top 2 *
from bills
where estimated = 1
order by billdate desc
You should be able to do a descensing sorted query on estimated = true and select top 2. I am not the best at SQL so i cant give exact language structure
Do you have a column for "statement number", e.g., if Q12008 was statement 28 for a particular customer, then Q22008's bill would be 29, Q32008's bill would be 30 (assuming quarterly billing). You could then check that the statement numbers were adjacent rather than having to do date manipulation.