SQL Server - Select data from one table based on a string value - sql

I am wanting to look up data from one of my database tables based on a varchar stored in another table.
We have a table of Manufacturers (M) and a table of Parameters (P), and rather than having to have a parameter per Manufacturer (as there is a lot of crossover between 60% of them), we thought it would be cleaner to have parameters per manufacturer where required, and for the other ones just have a set of default parameters.
This means that I cannot store ManufacturerID in both tables and create a simple link, instead I need to link using the ManufacturerName.
So I need to know if it is possible to Link via ManufacturerName, and where there is no match look in the Parameters table (P) for Manufacturer 'Default'.
As part of the link, we are also including:
P.CategoryID = M.CategoryID AND (P.PriceFrom <= M.BasePrice AND
P.PriceTo >= M.BasePrice)
Here is the data structures:
Manufacturers (M):
ManufacturerID, ManufacturerName, CategoryID, BasePrice
Parameters (P)
CategoryID, ManufacturerName, PriceFrom, PriceTo, Percentage
Here is some sample data:
Manufacturers (M):
ManufacturerID | ManufacturerName | CategoryID | BasePrice
3 | Apple | 1 | 150.00
3 | Apple | 9 | 99.99
10 | HTC | 1 | 50.00
15 | Nokia | 1 | 25.00
19 | Samsung | 1 | 80.00
Parameters (P):
CategoryID | ManufacturerName | PriceFrom | PriceTo | Percentage |
1 | Samsung | 0.00 | 99.99 | 50% |
1 | Apple | 0.00 | 99.99 | 55% |
1 | Apple | 100.00 | 149.99 | 45% |
9 | Apple | 0.00 | 99.99 | 65% |
1 | Default | 0.00 | 99.99 | 60% |
So we still need to just return 1 result in each link.
Any suggestions of guidance much appreciated.
Thanks in advance.

As per the conditions you have posted a Left Join should serve the purpose:
select M.ManufacturerID, P.ManufacturerName, M.CategoryID
from Manufacturers M
left join Parameters P On P.CategoryID = M.CategoryID AND (P.PriceFrom <= M.BasePrice AND P.PriceTo >= M.BasePrice)
Please post your table structure here if I have missed something: SQL Fiddle

Without a table structure to work with is difficoult but something within the line of
SELECT m.name Manufacturer, p.name, p.value, p.Manufacturer alias
FROM Manufacturers m
INNER JOIN Parameter p ON (m.name = p.Manufacturer)
UNION ALL
SELECT m.name Manufacturer, p.name, p.value, p.Manufacturer alias
FROM Manufacturers m
LEFT JOIN Parameter p on p.Manufacturer = 'default'
WHERE m.name + p.name not in (SELECT m.name + p.name
FROM Manufacturers m
INNER JOIN Parameter p
ON (m.name = p.Manufacturer)
)
ORDER BY 1, 2
Can get what you need. The first part get you the linked part, the second part get the default value only for the parameters name that don't have already a match.
This is a SQLFiddle with a few data and the query

Ok, so it turns out this was another situation where I was overthinking the problem.
I managed to get this to work by having the following Select statement nested within another select:
(SELECT
TOP(1) Percentage
FROM
Parameters AS P
WHERE
P.CategoryID = R.CategoryID
AND (P.PriceFrom <= M.BasePrice AND P.PriceTo >= M.BasePrice)
AND (P.ManufacturerName = M.ManufacturerName OR P.ManufacturerName = 'Default')
ORDER BY
CASE WHEN P.ManufacturerName = 'Default' THEN 'ZZZZZ' ELSE P.ManufacturerName END
) AS Markup
Many thanks for your help.

Related

Include zero counts when grouping by multiple columns and setting filters

I have a table (tbl) containing category (2 categories), impact (3 impacts), company name and date for example:
category | impact | company | date | number
---------+----------+---------+-----------|
Animal | Critical | A | 12/31/1999|1
Book | Critical | B | 12/31/2000|2
Animal | Minor | C | 12/31/2001|3
Book | Minor | D | 12/31/2002|4
Animal | Medium | E | 1/1/2003 |5
I want to get the count of records for each category and impact and be able to add rows with zero count and also be able to filter by company and date.
In the example result set below, the count result is 1 for category = Animal and company = A. The rest is 0 records and only the Critical and Medium impacts appear
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
I've looked at the responses to similar questions by using joins however, adding a WHERE clause doesn't include the zero records.
I also tried doing outer joins but it doesn't produce desired output. For example
select a.impact, b.category, ISNULL(count(b.impact), 0) from tbl a
left outer join tbl b
on b.number = a.number
and (a.category = 'Animal' and a.company in ('A'))
group by a.impact, b.category
produces
impact | category | count
---------+------------+--------
Medium | NULL | 0
Medium | Animal | 1
Critical | NULL | 0
Minor | NULL | 0
but the desired output should be
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
Animal | Minor | 0
Any help will be appreciated. Answers to associated questions don't have filtering so I will appreciate if someone can help with a query to produce desired output.
You need a master table with all the possible combinations of Categories and Impacts for this. Then Left join your table with the master and do the aggregation. Something like below
;WITH CAT
AS
(
SELECT
category
FROM Tbl
GROUP BY category
),
IMP
AS
(
SELECT
Impact
FROM Tbl
GROUP BY Impact
),MST
AS
(
SELECT
*
FROM CAT
CROSS JOIN IMP
)
SELECT
MST.category,
MST.Impact,
COUNT(T.Number)
FROM MST
LEFT JOIN Tbl T
ON MST.category = T.category
AND MST.Impact = T.Impact
AND T.Company = 'A'
WHERE MST.Category = 'Animal' GROUP BY MST.category,
MST.Impact

SQL Server avoid repeat same joins

I´m doing the query below where I´m repeating the same joins multiple times, there is a better way to do it? (SQL Server Azure)
Ex.
Table: [Customer]
[Id_Customer] | [CustomerName]
1 | Tomy
...
Table: [Store]
[Id_Store] | [StoreName]
1 | SuperMarket
2 | BestPrice
...
Table: [SalesFrutes]
[Id_SalesFrutes] | [FruteName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Orange | 1 | 1
...
Table: [SalesVegetable]
[Id_SalesVegetable] | [VegetableName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Pea | 1 | 2
...
Select * From [Customer] as C
left join [SalesFrutes] as SF on SF.[Fk_Id_Customer] = C.[Id_Customer]
left join [SalesVegetable] as SV on SV.[Fk_Id_Customer] = C.[Id_Customer]
left join [Store] as S1 on S1.[Id_Store] = SF.[Fk_Id_Store]
left join [Store] as S2 on S1.[Id_Store] = SV.[Fk_Id_Store]
In my real case, I have many [Sales...] to Join with [Customer] and many other tables similar to [Store] to join to each [Sales...]. So it starts to scale a lot the number on joins repeating. There is a better way to do it?
Bonus question: I do like also to have FruteName, VegetableName, StoreName, and each Food table name under the same column.
The Expected Result is:
[CustomerName] | [FoodName] | [SalesTableName] | [StoreName]
Tomy | Orange | SalesFrute | SuperMarket
Tomy | Pea | SalesVegetable | BestPrice
...
Thank you!!
So based on the information provided, I would have suggested the below, to use a cte to "fix" the data model and make writing your query easier.
Since you say your real-world scenario is different to the info provided it might not work for you, but could still be applicable if you have say 80% shared columns, you can just use placeholder/null values where relevant for unioning the data sets and still minimise the number of joins eg to your store table.
with allSales as (
select Id_SalesFrutes as Id, FruitName as FoodName, 'Fruit' as SaleType, Fk_Id_customer as Id_customer, Fk_Id_Store as Id_Store
from SalesFruits
union all
select Id_SalesVegetable, VegetableName, 'Vegetable', Fk_Id_customer, Fk_Id_Store
from SalesVegetable
union all... etc
)
select c.CustomerName, s.FoodName, s.SaleType, st.StoreName
from Customer c
join allSales s on s.Id_customer=c.Id_customer
join Store st on st.Id_Store=s.Id_Store

Can i merge the result of two queries?

I'm trying to retrieve some statistics from my database, to be concrete i'm look to show how many todo's is completed vs the total of a checklist.
The structure is as follows
A category has many Cards, has many checklists, has many assessments.
I can get the amount of assessments or completed assessments with the following query.
SELECT count(a.id) AS completed_count, a.checklist_id, ca.category_id
FROM assessments a
JOIN checklists ch ON ch.id = a.checklist_id
JOIN cards ca ON ca.id = ch.card_id
WHERE a.complete
GROUP BY a.checklist_id, ca.category_id;
This will give me something like this.
completed_count | checklist_id | category_id
-----------------+--------------+-------------
2 | 3 | 2
1 | 2 | 2
2 | 5 | 3
I could then do a query, to get the total amount, by removing the WHERE a.complete, and write some code that matches the two results.
But what i really want, is a result like this.
completed_amount | total_amount | checklist_id | category_id
------------------+--------------+--------------+-------------
2 | 2 | 3 | 2
1 | 1 | 2 | 2
2 | 2 | 5 | 3
I just can't wrap my head around, how i can achieve that.
I think conditional aggregation does what you want:
SELECT sum( (a.complete)::int ) AS completed_count,
count(*) as total_count
a.checklist_id, ca.category_id
FROM assessments a JOIN
checklists ch
ON ch.id = a.checklist_id JOIN
cards ca
ON ca.id = ch.card_id
GROUP BY a.checklist_id, ca.category_id;

SQL select only highest date

For a project I want to generate a price list.
I want to get only the latest prices from each supplier for each article.
There are just those two tables.
Table articles
ARTNR | TXT | ACTIVE | SUPPLIER
------------------------------------------
10 | APPLE | Y | 10
20 | ORANGE | Y | 10
30 | KEYBOARD | N | 20
40 | ORANGE | Y | 20
50 | BANANA | Y | 10
60 | CHERRY | Y | 10
Table prices
ARTNR | PRCGRP | PRCDAT | PRICE
--------------------------------------
10 | 10 | 01-Aug-10 | 2.1
10 | 10 | 05-Aug-11 | 2.2
10 | 10 | 21-Aug-12 | 2.5
20 | 0 | 01-Aug-10 | 2.1
20 | 10 | 09-Aug-12 | 2.3
10 | 10 | 14-Aug-13 | 2.7
This is what I have so far:
SELECT
ARTICLES.[ARTNR], ARTICLES.[TXT], ARTICLES.[ACTIVE], ARTICLES.[SUPPLIER], PRICES.PRCGRP, PRICES.PRCDAT, PRICES.PRICE
FROM
ARTICLES INNER JOIN PRICES ON ARTICLES.ARTNR = PRICES.ARTNR
WHERE
(
(ARTICLES.[ACTIVE]="Y") AND
(ARTICLES.[SUPPLIER]=10) AND
(PRICES.PRCGRP=0) AND
(PRICES.PRCDAT=(SELECT MAX(PRCDAT) FROM PRICES as art WHERE art.ARTNR = PRICES.artnr) )
)
ORDER BY ARTICLES.ARTNR
;
It is okay to choose just one supplier each time, but I want the max price.
The problem is:
Lots of articles do not show up with the query above,
but I cannot figure out what is wrong.
I can see that they should be in the resultset when I leave out the subselect on max prcdat.
What is wrong?
Your subquery to get the latest price does not take the other conditions into account, that is when you're getting the latest price, you may get a price in another price group or that is not active. When you join that against the filtered list that has no inactive prices and only prices in a single price group, you get no hits that exist in both.
Either you need to duplicate or - better - move your conditions inside the subquery to get the best price under the conditions. I can't test against access, but something like this should be possible if the SQL is not too limited;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
JOIN (
SELECT a.artnr, MAX(p.prcdat) prcdat
FROM articles a JOIN prices p ON a.artnr = p.artnr
WHERE a.active='Y' AND a.supplier=10 AND p.prcgrp=10
GROUP BY a.artnr) z
ON a.artnr = z.artnr AND p.prcdat = z.prcdat
ORDER BY a.ARTNR
If the SQL support in access won't allow a join with a subquery, you can just move the conditions inside your existing subquery, something like;
SELECT a.artnr, a.txt, a.active, a.supplier, p.prcgrp, p.prcdat, p.price
FROM articles a INNER JOIN prices p ON a.ARTNR = p.ARTNR
WHERE p.prcdat = (
SELECT MAX(p2.prcdat)
FROM articles a2 JOIN prices p2 ON a2.artnr = p2.artnr
WHERE a.artnr = a2.artnr AND a2.active='Y' AND a2.supplier=10 AND p2.prcgrp=10
)
ORDER BY a.ARTNR;
Note that due to limitations in identifying a unique price (no primary key in prices), the queries may give duplicates if several prices for the same article have the same prcdat. If that's a problem, you'll probably need to duplicate your conditions outside the subquery too.

Multiple select or distinct

I am new to sql so looking for a little help - got the first part down however I am having issues with the second part.
I got the three tables tied together. First I needed to tie tblPatient.ID = tblPatientVisit.PatientID together to eliminate dups which works
Now I need to take those results and eliminate dups in the MRN but my query is only returning one result which is WRONG - LOL
Query
select
tblPatient.id,
tblPatient.firstname,
tblPatient.lastname,
tblPatient.dob,
tblPatient.mrn,
tblPatientSmokingScreenOrder.SmokeStatus,
tblPatientVisit.VisitNo
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
and tblPatient.ID in(
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I')
Actual Results:
ID | firstName | LastName | DOB | MRN | SmokeStatus | VisitNO
12 | Test Guy | Today | 12/12/1023 | 0015396 | Never Smoker | 0013957431
Desired Results:
90 | BOB | BUILDER | 02/24/1974 | 0015476 | Former Smoker | 0015476001
77 | DORA | EXPLORER | 06/04/1929 | 0015463 | Never Smoker | 0015463001
76 | MELODY | VALENTINE | 09/17/1954 | 0015461 | Current | 0015461001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Current | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Never Smoker | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Former Smoker | 0015415001
12 | Test Guy | Today | 12/12/1023 | 0015345 | Never Smoker | 0013957431
Anyone have any suggestions on how I go down to the next level and get all the rows with one unique MRN. From the data above I should have 5 in my list. Any help would be appreciated.
Thanks
If I had to guess -- The only thing that looks odd (but maybe it's OK) is that you're comparing patient.ID from your parent query to patient.mrn in the subquery.
Beyond that -- things to check:
(1)
Do you get all your patients with the inner query?
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I'
(2)
What is your patient type for the missing records? (Your filtering it to tblPatientVisit.PatientType = 'I' -- do the missing records have that patient type as well?)
Perhaps you need to invert the logic here. As in,
Select Distinct
patients.mrn1
From (select
tblPatient.id as id1,
tblPatient.firstname as firstname1,
tblPatient.lastname as lastname1,
tblPatient.dob as DOB1,
tblPatient.mrn as mrn1,
tblPatientSmokingScreenOrder.SmokeStatus as SmokeStatus1,
tblPatientVisit.VisitNo as VisitNo1,
tblPatientVisit.PatientType as PatientType1,
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
) as patients
where
isdate(patients.DOB1) = 1
and Convert(date,patients.DOB1) <'12/10/2000'
and patients.PatientType1 = 'I');
Cleaned it up a bit and I think they were right. MRN wont match patient id, at least not from your example data. You should not need an inner query. This query should give you what you want.
SELECT DISTINCT
p.id,
p.firstname,
p.lastname,
p.dob,
p.mrn,
s.SmokeStatus,
v.VisitNo
FROM
tblPatient p
JOIN tblPatientVisit v ON p.id = v.patientId
JOIN tblPatientSmokingScreenOrder s ON v.id = s.visitId
WHERE
isdate(p.DOB) = 1
AND CONVERT(date,p.DOB) <'12/10/2000'
AND v.PatientType = 'I'