Create function that returns table in SQL - sql

I wanted to create view with some logic like using (for loop , if .. else) but since that's not supported in SQL
I thought of creating table function that takes no parameter and returns a table.
I have a table for orders as below
OrderId Destination Category Customer
----------------------------------------
6001 UK 5 Adam
6002 GER 3 Jack
And table for tracking orders as below
ID OrderID TrackingID
-----------------------
1 6001 1
2 6001 2
3 6002 2
And here are the types of tracking
ID Name
--------------
1 Processing
2 Shipped
3 Delivered
As you can see in tracking order, The order number may have more than one record depending on how many tracking events occurred.
We have more than 25 tracking types that I didn't include here. which means one order can exist 25 times in tracking order table.
Now with that being said , My requirements is to create view as below with condition that an order must belong to 5 or 3 category ( we have more than 15 categories).
And whenever I run the function it must return the updated information.
So for example, when new tracking occurs and it's inserted in tracking order , I want to run my function and see the update in the corresponding flag column (e.g isDelivered).
I'm really confused on what is the best way to achieve this. I don't need the exact script i just need to understand the way to achieve it as i'm not very familiar with SQL

It could be done with a crosstab query using conditional aggregation. Something like this
select o.OrderID,
max(case when tt.[Name]='Processing' then 1 else 0 end) isPrepared,
max(case when tt.[Name]='Shipped' then 1 else 0 end) isShipped,
max(case when tt.[Name]='Delivered' then 1 else 0 end) isDelivered
from orders o
join tracking_orders tro on o.OrderID=tro.OrderID
join tracking_types tt on tro.TrackingID=tt.TrackingID
where o.category in(3, 5)
group by o.OrderID;
[EDIT] To break out Category 3 orders, 3 additional columns were added to the cross tab.
select o.OrderID,
max(case when tt.[Name]='Processing' then 1 else 0 end) isPrepared,
max(case when tt.[Name]='Shipped' then 1 else 0 end) isShipped,
max(case when tt.[Name]='Delivered' then 1 else 0 end) isDelivered,
max(case when tt.[Name]='Processing' and o.category=3 then 1 else 0 end) isC3Prepared,
max(case when tt.[Name]='Shipped' and o.category=3 then 1 else 0 end) isC3Shipped,
max(case when tt.[Name]='Delivered' and o.category=3 then 1 else 0 end) isC3Delivered
from orders o
join tracking_orders tro on o.OrderID=tro.OrderID
join tracking_types tt on tro.TrackingID=tt.TrackingID
where o.category in(3, 5)
group by o.OrderID;

Related

GROUP BY in a temp table

I'm working on a stored procedure and altering a temp table to spit out data depending on a specific condition. This stored procedure is quite complex and has like 40 columns, however for this specific issue I simply need to group the records by the DESCR column. The other 37 columns all have the same value so it shouldn't be an issue but in this case how can I group them by their DESCR and consolidate the LATEFEE and CHG values?
The query essentially looks like this:
SELECT [columns1], [column38], t.descr t.chg, t.lateFee, (t.chg + t.lateFee) as Total
FROM #tmpStatement t
INNER JOIN dbo.Association a ON t.AssocID = a.AssocID
INNER JOIN dbo.Company c ON a.CompanyID = c.CompanyID
GROUP BY
[all columns]
If I remove the t.chg or the t.lateFee columns from the GROUP BY clause then SQL tells me that they are invalid in the SELECT list since they're not included in the GROUP BY, however I still want those values in my table.
Currently this is the outcome of the table:
DESCR CHG LATEFEE TOTAL
=================================
Assessment 1 0 1
Spec 0 2 2
Spec 3 0 3
Assessment 0 5 5
Res 3 0 3
 
What I want the expected result to look like:
DESCR CHG LATEFEE TOTAL
=================================
Assessment 1 5 6
Spec 3 2 5
Res 3 0 3
 
Looking at the sample data, you probably need something as simple as:
select descr, other, sum(chg) as chg, sum(latefee) as latefee, sum(chg) + sum(latefee) as total
from t
group by descr, other

Creating row with different where

I have this code to get the number of users of all items in the list and the average level.
select itemId,count(c.characterid) as numberOfUse, avg(maxUpgrade) as averageLevel
from items i inner join characters c on i.characterId=c.characterId
where itemid in (22001,22002,22003,22004,22005,22006,22007,22008,22009,22010,22011,22012,22013,22014,22015,22016,22030,22031,22032,22033,22034,22035,22036,22037,22038,22039,22040,22041,22042,22050,22051,22052,22053,22054,22055,22056,22057,22058,22059,22060,22070,22071,22072,22073,22074,22075,22076,22077,22085,22086,22087,22091,22092)
and attached>0
group by itemId
It does is creating a row for the rune id, one for the number of users, and one for the average-level people who upgrade it, and it does that for all players of the server.
I would like to create a new column every 10 levels to have stats every 10 levels, so I can see what item is more used depending on player level. The item level depending on the level, so the way I do to select only a certain level is using WHERE itemid>0 and itemid<10, and I do that every 10 levels, copy data, and push them in a google sheet.
So I would like a result with columns :
itemid use_1-10 avg_level_1-10 use_11-20 avg_level_21-30 etc...
So I could copy all the results at once and not having to do the same process 15 times.
If I am following this correctly, you can do conditional aggregation. Assuming that a "level" is stored in column level in table characters, you would do:
select i.itemId,
sum(case when c.level between 1 and 10 then 1 else 0 end) as use_1_10,
avg(case when c.level between 1 and 10 then maxUpgrade end) as avg_level_1_10,
sum(case when c.level between 11 and 20 then 1 else 0 end) as use_11_20,
avg(case when c.level between 11 and 20 then maxUpgrade end) as avg_level_11_20,
...
from items i
inner join characters c on i.characterId = c.characterId
where i.itemid in (...) and attached > 0
group by i.itemId
Note: consider prefixing column attached in the where clause with the table it belongs to, in order to avoid ambiguity.

DB2 SQL - Difference (minus) of CASE WHEN

I am trying to do the difference between 2 case when in a DB2 environment as follow:
select DISTINCT
TLORDER.BILL_NUMBER,
TLORDER.XCHARGES,
TLORDER.CHARGES,
TLORDER.DISTANCE,
TLORDER.CREATED_TIME,
TLORDER.DESTCITY,
(CASE WHEN ODRSTAT.STATUS = '5ARRCONS' THEN MAX(ORDSTAT.CHANGED)
END -
CASE WHEN ODRSTAT.STATUS = 'PICKD' THEN MIN(ODRSTAT.CHANGED)
END) AS DETENTION
FROM ODRSTAT
LEFT JOIN TLORDER ON ODRSTAT.ORDER_ID = TLORDER.DETAIL_LINE_ID
I tried a few variation of this concept using the various online ressources and found many answers for sums but not for difference using the same columns.
The goal is to substract the oldest date (in col. CHANGED) if status in Col. Status is 'pickd' from the newest date (in the same col CHANGED) if the status in Col. STATUS is '5arrcons'
Consider the following dataset:
Key ORDER_ID STATUS CHANGED
1 10 5ARRCONS 12/10/2017
2 10 OTHER 12/10/2017
3 10 PICKD 12/5/2017
4 10 OTHER 12/3/2017
5 10 PICKD 12/1/2017
In this case the wanted result from the CASE statement would be
MAX = 12/10/2017
Min = 12/1/2017
so (12/10/2017 - 12/1/2017) equals 9
9 is what I would want in what is returned
Any and all help will be appreciated.
thank you for your time
It is hard to tell exactly you want want. But, this may be what you want:
SELECT o.BILL_NUMBER, o.XCHARGES, o.CHARGES, o.DISTANCE, o.CREATED_TIME, o.DESTCITY,
(MAX(CASE WHEN os.STATUS = '5ARRCONS' THEN os.CHANGED END) -
MIN(CASE WHEN os.STATUS = 'PICKD' THEN os.CHANGED END)
) AS DETENTION
FROM ODRSTAT os JOIN
TLORDER o
ON os.ORDER_ID = o.DETAIL_LINE_ID
GROUP BY o.BILL_NUMBER, o.XCHARGES, o.CHARGES, o.DISTANCE, o.CREATED_TIME, o.DESTCITY;

Using Count distinct case in sql and group by multiple columns

I have a query that works great (listed below). The issue I am having is we have run into a patient that has had event on two different days and because I am grouping by the PATNUM, it is only showing it as one.
How can I get it to count 1 for each time if the PATNUM and SCHDT are different
Example:
PATNUM SCHDT
12345 30817
12345 30817
54321 30817
54321 30717
PATNUM 12345 should only count once while PATNUM 54321 should count twice.
My count statement is this:
SELECT ph.*, pi.*,
COUNT(DISTINCT CASE WHEN `SERVTYPE` IN ('INPT','INPFOP','INFOBS','IP') AND Complete ='7' THEN pi.PATNUM ELSE NULL END) AS count1,
COUNT(DISTINCT CASE WHEN `SERVTYPE` IN ('INPT','INPFOP','INFOBS','IP') AND Complete ='8' THEN pi.PATNUM ELSE NULL END) AS count2
FROM patientinfo as pi
INNER JOIN physicians as ph ON pi.SURGEON=ph.PName
WHERE PID NOT IN ('1355','988','767','1289','484','2784')
GROUP BY SURGEON
ORDER BY Dept,SURGEON ASC
Which columns do you want to see?
You can adjust your GROUP BY:
SELECT
ph.pname,
ph.specialty,
SUM(CASE WHEN complete = 7 THEN 1 ELSE 0 END) count1,
SUM(CASE WHEN complete = 8 THEN 1 ELSE 0 END) count2
FROM
(
SELECT
DISTINCT
surgeon,
patnum,
schdt,
complete,
servtype
FROM patientinfo
WHERE complete IN (7,8)
AND servtype IN ('INPT','INPFOP','INFOBS','IP')
AND pid NOT IN ('1355','988','767','1289','484','2784')
) pisub
INNER JOIN physicians ph ON pisub.surgeon = ph.pname
GROUP BY ph.pname, ph.specialty
ORDER BY ph.pname, ph.specialty;
Also, I would make a few suggestions:
If you're going to give your tables an alias, then use the alias when referring to any column in your query. I've made a guess here about some of your columns as to which table they come from (e.g. dept), so feel free to change it if it is not correct
You don't need to select all records from both tables if you don't need them
The query won't run if you don't GROUP BY all columns you're selecting. I've written about this for Oracle and SQL in general, but actually in MySQL I think it does run but show incorrect results.

sql query that will get a distinct type, brand, and model, but get a count of how many duplicates were found

I have a table "Competitor" and here are some of its columns:
Type | Brand | Model | Date | Resolution | etc.
The table will have duplicate Model entries (with obviously same Brand as well, but possibly a different Type (two possible types: 'ProAV' and 'Disti')). I need to build a query that will output a table like this:
Top (ProAV) | Top (Disti) | Last Occurrence | Brand | Model | Resolution | etc.
Basically I need a query that will get a distinct type, brand, and model, but get a count of how many duplicates were found and put that number in either Top (ProAV) or Top (Disti), whichever Type it has. I would need to pull the most recent (given Date) out of the duplicates, so that I can put its Date as the Last Occurrence field. I hope this makes sense, let me know if it doesn't.
SELECT SUM(CASE WHEN Type = 'ProAV' THEN 1 ELSE 0 END) AS TopProAV,
SUM(CASE WHEN Type = 'Disti' THEN 1 ELSE 0 END) AS TopDisti,
MAX(Date) AS LastOccurence,
Brand, Model, Resolution
FROM Competitor
GROUP BY Brand, Model, Resolution
EDIT: Based on the comment, you could use a subquery or CTE to accomplish what you want. Something like:
WITH cteMaxDate AS (
SELECT SUM(CASE WHEN Type = 'ProAV' THEN 1 ELSE 0 END) AS TopProAV,
SUM(CASE WHEN Type = 'Disti' THEN 1 ELSE 0 END) AS TopDisti,
MAX(Date) AS LastOccurence,
Brand, Model, Resolution
FROM Competitor
GROUP BY Brand, Model, Resolution
)
SELECT md.TopProAV, md.TopDisti,
md.LastOccurentce,
md.Brand, md.Model, md.Resolution,
c.AdditionalColumn1, c.AdditionalColumn2
FROM cteMaxDate md
INNER JOIN Competitor c
ON md.Brand = c.Brand
AND md.Model = c.Model
AND md.Resolution = c.Resolution
AND md.LastOccurence = c.Date
Do you have a limited number of Types? In this case you can solve your problem using pivot
More specifically, for the table
Type Model
---- -----
A X
B X
C Y
A Z
NULL NULL
you run this query
Select Model, [A], [B], [C]
From
(select Model, Type
from dbo.Competitor) as SourceTable
PIVOT
(Count([Type]) for [Type] in ([A], [B], [C])) as PivotTable
to get
Model A B C
------ - - -
X 1 1 0
Y 0 0 1
Z 1 0 0