SQL query - joins + counting - sql

Here's my problem: I have a query that joins multiple tables to show details of some orders. The query result in a table with columns:
order ID | name | count | price | location | date
It's a hospital database and what i want to do is to add another column that says how many patients were at that location at given date.
There's another table that shows patient stays - I need to count those.
patient ID | location | dateFrom | dateTo
The thing is that the STAYS table shows 2 dates - FROM and TO so I need to count every patient that was present at given location (ward) when order was placed.
Here's the initial query I need to update:
SELECT
AP_ZAMPOZ.ID_TOW AS IDTowaru, --merchandiseID
GMSL_TOW.NAZWA_TOW AS Nazwa, --name
GMSL_TOW.MNOZNIK_SYN AS Mnoznik, --quantity
AP_ZAMPOZ.ZAM_CENA_S AS Cena, --price
AP_ZAMPOZ.ZAM_IL AS Ilosc, --count
AP_ZAMNAG.ZAM_DATE AS DataZam, --date
GMSL_MAG.NAZWA_MAG AS Magazyn, --location
APSL_TOW_PROD.PROD_NAZWA AS Producent, --producer
APSL_TOW_ATC.NAZWA AS Grupa -group
FROM
AP_ZAMPOZ
JOIN
GMSL_TOW ON AP_ZAMPOZ.ID_TOW = GMSL_TOW.ID_TOW
JOIN
AP_ZAMNAG ON AP_ZAMNAG.ZAM_ID_NAG = AP_ZAMPOZ.ZAM_ID_NAG
JOIN
GMSL_MAG ON AP_ZAMNAG.ID_MAG = GMSL_MAG.ID_MAG
JOIN
APSL_TOW ON AP_ZAMPOZ.ID_TOW = APSL_TOW.ID_TOW
LEFT JOIN
APSL_TOW_PROD ON APSL_TOW.ID_PROD = APSL_TOW_PROD.ID_PROD
LEFT JOIN
APSL_TOW_ATC ON APSL_TOW.KOD = APSL_TOW_ATC.KOD
The table with stays is called POBYT and has these relevant columns:
| ID_POB (ID) | IDK_JOS (location identifier) | DT_OD (date From) | DT_TO (date To)
Rows that I would like to see should look like those in my present query + number of patients at given location at given date.
Anyone have any ideas how to achieve this? I'm stuck...

problem solved by adding subquery as another SELECT column.
Heres the code
SELECT
.
.
.
APSL_TOW_ATC.NAZWA AS Grupa
(SELECT Count(*)
FROM pobyt
WHERE (TO_DATE( AP_ZAMNAG.ZAM_DATE, 'YY/MM/DD') >= TO_DATE(DT_OD, 'YY-MM-DD') AND (TO_DATE( AP_ZAMNAG.ZAM_DATE, 'YY/MM/DD') <= TO_DATE(DT_DO, 'YY-MM-DD') OR dt_do IS NULL))
AND IDK_JOS = GMSL_MAG.KOD_MAG) AS LiczbaPacjentow --no. of patients at given date
FROM AP_ZAMPOZ
.
.
.
Works great.

Related

Newbie struggling to join 3 tables

I have 3 tables of purchases in the following format:
date | company_id | apple_txn_amt
date | company_id | orange_txn_amt
date | company_id | pear_txn_amt
There are multiple purchases/sales daily for many companies. I'm trying to join and group so there is only 1 date per company along with total fruit balance:
date | company_id | total_apple_balance | total_apple_orange_balance | total_pear_balance
I have built a query for a similar case earlier, and used 2 joins. But this was for only one company's data so I was only joining on date=date for each table. Process for each table was: gather buys, sells, union those two, union to a new table with generate_series() to insert 0s for days missing, calculate daily delta, and group by day to have a running total. Then something like:
SELECT
apple.day
apple.total
orange.total
pear.total
(apple + orange + pear) AS total_fruit
FROM apple
JOIN orange ON orange.date = apple.date
JOIN pear ON pear.date = apple.date
ORDER BY day
It's like I need to JOIN ON date and company id but from what I can tell this isn't possible.
Should I approach this in a different way?
Sure you can add the company_id like
SELECT
apple.day
apple.total
orange.total
pear.total
(apple.total + orange.total + pear.total) AS total_fruit
FROM apple
JOIN orange ON orange.date = apple.date AND orange.company_id = apple.company_id
JOIN pear ON pear.date = apple.date AND pear.company_id = apple.company_id
ORDER BY day
But the design of your database isn't right, if circumstances don't require it.
you would not have 3 tables, you would have only one with Fruit type as another column, to differentiate them

Link data from third table to a 1st and a 2nd table

I got a little problem and I wonder if there is a solution.
Tried finding something existing, to no avail.
So here is my code :
Select
Order.AccountID,
Order.UserID,
Order.OrderID,
Order.OrderDate,
User.UserName,
Note.NoteID,
Note.UserID,
Note.NoteDate,
Note.Text
FROM
Order
INNER JOIN User ON (Order.UserID=User.UserID)
LEFT OUTER JOIN Note ON (Order.AccountID=Note.AccountID)
WHERE
Order.OrderDate >="2016-01-01"
AND
Order.OrderDate <= (current date -1 day)
AND
Note.NoteID IN (21,41,89)
AND
Note.NoteDate >="2016-01-01"
AND
Note.NoteDate<= (current date -1 day)
GROUPBY
Order.AccountID,
Order.UserID,
Order.OrderID,
Order.OrderDate,
User.UserName,
Note.UserID,
Note.NoteDate,
Note.Text
So we have 3 tables :
The Order Table : Contains Orders. They are saved with the userID who did the order.
The User Table : Has the UserIDs and the Username. I already used the table to link the usernames to the userIDs in the orders.
The Note Table : Contains notes on the account and the userID who wrote the note. I want to also get the usernames of the userIDS on the notes.
The one who did the order, is not necessary the one who wrote the note...
Question : How do I get the usernames Linked to my notes as they are already linked to my orders ?
Tables :
Order
AccountID | OrderID | OrderDate | UserID
User
UserID | Username
Note
NoteID | NoteDate | Text | UserID
Something like :
AccountID | OrderID | OrderDate | Order.UserID | Order.Username | NoteID | NoteDate | Text | UserID | Note.Username
How do ?
If I understand your question correctly, you just need to do the same as what you have already done with an alias:
Select
Order.AccountID,
Order.UserID,
Order.OrderID,
Order.OrderDate,
User.UserName,
Note.NoteID,
Note.UserID,
Note.NoteDate,
Note.Text,
NoteWriter.UserName as 'NoteWriter_Username'
FROM
INNER JOIN User ON (Order.UserID=User.UserID)
LEFT OUTER JOIN Note ON (Order.AccountID=Note.AccountID)
LEFT OUTER JOIN user NoteWriter on NoteWriter.userid = Note.userID
WHERE
Order.OrderDate >="2016-01-01"
AND Order.OrderDate <= (current date -1 day)
AND Note.NoteID IN (21,41,89)
AND Note.NoteDate >="2016-01-01"
AND Note.NoteDate<= (current date -1 day)
The trick is to alias the table you are joining on so that you can reference it. I've aliased the new join as NoteWriter, who will represent the user that added the note. I've also aliased the new column in your select, just to show you that you can name things a bit easier in your result set.
I've dropped the group by as it won't change anything. Unless you have some weird data, that is.

can't join table on request

I'm having issues on table joins in a sql query. I've looked it up on SO but nothing matched my problem.
Here are the two tables:
Charge
| tache_id (int) | semaine_id (int) | login (varchar) | charge (float) |
Tache
| tache_id (int) | type_id (int) | charge (float) |
Basically, every task (Tache) has a charge attribute which represents the amount of work (in days) necessary to complete the task.
The user can then plan it on several weeks (Charge).
What I want to do is display all the tasks that haven't been completely planned, and then display (in an other window) those who are.
I thought of doing something like this:
select Tache.tache_id, Tache.charge
left join Charge on Charge.tache_id = Tache.tache_id
where sum(Charge.charge) < Tache.charge
group by Tache.tache_id
But I get a 'invalid use of group function' error
something on these lines, if tache.tache_id is unique:
select Tache.tache_id, max(Tache.charge)
from tache
left join Charge on Charge.tache_id = Tache.tache_id
group by Tache.tache_id
having sum(Charge.charge) < max(Tache.charge)
What you meant is a having clause, you can use aggregate functions in the where clause..
select Tache.tache_id, max(Tache.charge)
from Tache
left join Charge on Charge.tache_id = Tache.tache_id
group by Tache.tache_id
having sum(Charge.charge) < max(Tache.charge)

SQL: SUM of MAX values WHERE date1 <= date2 returns "wrong" results

Hi stackoverflow users
I'm having a bit of a problem trying to combine SUM, MAX and WHERE in one query and after an intense Google search (my search engine skills usually don't fail me) you are my last hope to understand and fix the following issue.
My goal is to count people in a certain period of time and because a person can visit more than once in said period, I'm using MAX. Due to the fact that I'm defining people as male (m) or female (f) using a string (for statistic purposes), CHAR_LENGTH returns the numbers I'm in need of.
SELECT SUM(max_pers) AS "People"
FROM (
SELECT "guests"."id", MAX(CHAR_LENGTH("guests"."gender")) AS "max_pers"
FROM "guests"
GROUP BY "guests"."id")
So far, so good. But now, as stated before, I'd like to only count the guests which visited in a certain time interval (for statistic purposes as well).
SELECT "statistic"."id", SUM(max_pers) AS "People"
FROM (
SELECT "guests"."id", MAX(CHAR_LENGTH("guests"."gender")) AS "max_pers"
FROM "guests"
GROUP BY "guests"."id"),
"statistic", "guests"
WHERE ( "guests"."arrival" <= "statistic"."from" AND "guests"."departure" >= "statistic"."to")
GROUP BY "statistic"."id"
This query returns the following, x = desired result:
x * (x+1)
So if the result should be 3, it's 12. If it should be 5, it's 30 etc.
I probably could solve this algebraic but I'd rather understand what I'm doing wrong and learn from it.
Thanks in advance and I'm certainly going to answer all further questions.
PS: I'm using LibreOffice Base.
EDIT: An example
guests table:
ID | arrival | departure | gender |
10 | 1.1.14 | 10.1.14 | mf |
10 | 15.1.14 | 17.1.14 | m |
11 | 5.1.14 | 6.1.14 | m |
12 | 10.2.14 | 24.2.14 | f |
13 | 27.2.14 | 28.2.14 | mmmmmf |
statistic table:
ID | from | to | name |
1 | 1.1.14 | 31.1.14 |January | expected result: 3
2 | 1.2.14 | 28.2.14 |February| expected result: 7
MAX(...) is the wrong function: You want COUNT(DISTINCT ...).
Add proper join syntax, simplify (and remove unnecessary quotes) and this should work:
SELECT s.id, COUNT(DISTINCT g.id) AS People
FROM statistic s
LEFT JOIN guests g ON g.arrival <= s."from" AND g.departure >= s."too"
GROUP BY s.id
Note: Using LEFT join means you'll get a result of zero for statistics ids that have no guests. If you would rather no row at all, remove the LEFT keyword.
You have a very strange data structure. In any case, I think you want:
SELECT s.id, sum(numpersons) AS People
FROM (select g.id, max(char_length(g.gender)) as numpersons
from guests g join
statistic s
on g.arrival <= s."from" AND g.departure >= s."too"
group by g.id
) g join
GROUP BY s.id;
Thanks for all your inputs. I wasn't familiar with JOIN but it was necessary to solve my problem.
Since my databank is designed in german, I made quite the big mistake while translating it and I'm sorry if this caused confusion.
Selecting guests.id and later on grouping by guests.id wouldn't make any sense since the id is unique. What I actually wanted to do is select and group the guests.adr_id which links a visiting guest to an adress databank.
The correct solution to my problem is the following code:
SELECT statname, SUM (numpers) FROM (
SELECT statistic.name AS statname, guests.adr_id, MAX( CHAR_LENGTH( guests.gender ) ) AS numpers
FROM guests
JOIN statistics ON (guests.arrival <= statistics.too AND guests.departure >= statistics.from )
GROUP BY guests.adr_id, statistic.name )
GROUP BY statname
I also noted that my database structure is a mess but I created it learning by doing and haven't found any time to rewrite it yet. Next time posting, I'll try better.

Access join on first record

I have two tables in an Access database, tblProducts and tblProductGroups.
I am trying to run a query that joins both of these tables, and brings back a single record for each product. The problem is that the current design allows for a product to be listed in the tblProductGroups table more than 1 - i.e. a product can be a member of more than one group (i didnt design this!)
The query is this:
select tblProducts.intID, tblProducts.strTitle, tblProductGroups.intGroup
from tblProducts
inner join tblProductGroups on tblProducts.intID = tblProductGroups.intProduct
where tblProductGroups.intGroup = 56
and tblProducts.blnActive
order by tblProducts.intSort asc, tblProducts.curPrice asc
At the moment this returns results such as:
intID | strTitle | intGroup
1 | Product 1 | 1
1 | Product 1 | 2
2 | Product 2 | 1
2 | Product 2 | 2
Whereas I only want the join to be based on the first matching record, so that would return:
intID | strTitle | intGroup
1 | Product 1 | 1
2 | Product 2 | 1
Is this possible in Access?
Thanks in advance
Al
This option runs a subquery to find the minimum intGoup for each tblProducts.intID.
SELECT tblProducts.intID
, tblProducts.strTitle
, (SELECT TOP 1 intGroup
FROM tblProductGroups
WHERE intProduct=tblProducts.intID
ORDER BY intGroup ASC) AS intGroup
FROM tblProducts
WHERE tblProducts.blnActive
ORDER BY tblProducts.intSort ASC, tblProducts.curPrice ASC
This works for me. Maybe this helps someone:
SELECT
a.Lagerort_ID,
FIRST(a.Regal) AS frstRegal,
FIRST(a.Fachboden) AS frstFachboden,
FIRST(a.xOffset) AS frstxOffset,
FIRST(a.yOffset) AS frstyOffset,
FIRST(a.xSize) AS frstxSize,
FIRST(a.ySize) AS frstySize,
FIRST(a.Platzgr) AS frstyPlatzgr,
FIRST(b.Artikel_ID) AS frstArtikel_ID,
FIRST(b.Menge) AS frstMenge,
FIRST(c.Breite) AS frstBreite,
FIRST(c.Tiefe) AS frstTiefe,
FIRST(a.Fachboden_ID) AS frstFachboden_ID,
FIRST(b.BewegungsDatum) AS frstBewegungsDatum,
FIRST(b.ErzeugungsDatum) AS frstErzeugungsDatum
FROM ((Lagerort AS a)
LEFT JOIN LO_zu_ART AS b ON a.Lagerort_ID = b.Lagerort_ID)
LEFT JOIN Regal AS c ON a.Regal = c.Regal
GROUP BY a.Lagerort_ID
ORDER BY FIRST(a.Regal), FIRST(a.Fachboden), FIRST(a.xOffset), FIRST(a.yOffset);
I have non unique entries for Lagerort_ID on the table LO_zu_ART. My goal was to only use the first found entry from LO_zu_ART to match into Lagerort.
The trick is to use FIRST() an any column but the grouped one. This may also work with MIN() or MAX(), but I have not tested it.
Also make sure to call the Fields with the "AS" statement different than the original field. I used frstFIELDNAME. This is important, otherwise I got errors.
Create a new query, qryFirstGroupPerProduct:
SELECT intProduct, Min(intGroup) AS lowest_group
FROM tblProductGroups
GROUP BY intProduct;
Then JOIN qryFirstGroupPerProduct (instead of tblProductsGroups) to tblProducts.
Or you could do it as a subquery instead of a separate saved query, if you prefer.
It's not very optimal, but if you're bringing in a few thousand records this will work:
Create a query that gets the max of tblProducts.intID from one table and call it qry_Temp.
Create another query and join qry_temp to the table you are trying to join against, and you should get your results.