Sequential Column Where Conditional - SQL / Access - sql

Suppose we have some data that looks like so
R_Id Nm Base Dest Proj Cust_id 201203 201202 201201
MRBR Bob LONDON UK Project1 1 0 0 0
MRBU Frank LONDON London Project2 2 11.68 0 248.93
MRBU Frank LONDON UK Project3 1 7.4 4.8 0
MRGB Barry GUILDFORD Hull Project4 1 50.36 12.85 48.92
MRGB Barry GUILDFORD Project5 1 0 177.31 0
MRGB Barry GUILDFORD INTL Project6 3 0 331.08 0
And suppose we have a lot more columns than above, but we've limited to a few for now.
I want to be able to use a where statment to only show rows where the row needs further investigation. This is done by saying "Where are there more than two large numbers next to each other in a row?" So I need to count the number of rows where the number is large.
The output should look like such, where I've explained what filtering I'm doing.
R_Id Nm Base Dest Proj Cust_id 201203 201202 201201
MRBR Bob LONDON UK Project1 1 "Numbers not Large"
MRBU Frank LONDON London Project2 2 11.68 248.93 0
MRBU Frank LONDON UK Project3 1 "Numbers not Large"
MRGB Barry GUILDFORD Hull Project4 1 50.36 12.85 48.92
MRGB Barry GUILDFORD Project5 1 "Too few adjacent numbers"
MRGB Barry GUILDFORD INTL Project6 3 "Too few adjacent numbers"
It's the case where there are too few adjacent numbers I'm trying to filter for. I need to count the numer of adjacent (or every other!) numbers in those specific columns.
I've looked at this question: Multiple Column Conditional Count SQL, but I don't think I can use Count(*) as I get this error: You tried to execute a query thaty does not include the speicified expression 'AT_RISK?' as part of aggregate function. At risk is a column that just stores Yes/No and lives to the left of R_Id (not included above for brevity)
Can anyone help or at least point me in the right direction please? I'd really appreciate it. I've read the question above, and I've looked at how to use count in general, but this is really stumping me.

Well I can think of a somewhat ugly solution to this question, but it involves the use of a custom VBA function.
Concatenate and Test String
Your SQL statement should be something like:
SELECT * FROM tblName
WHERE IsSeqHigh([201203] & ";" & [201202] & ";" & ..., 1000);
And then, in a VBA module we define:
Public Function IsSeqHigh(seq As String, thres As Double) As Boolean
IsSeqHigh = False
Dim valStrs() As String
valStrs = Split(seq, ";")
For n = 1 To UBound(valStrs) - 1
If (valStrs(n) >= thres) And (valStrs(n + 1) >= thres) Then
IsSeqHigh = True
Exit For
End If
Next n
End Function
Another approach
Alternatively, if your schema is fixed and unlikely to change - and you have a primary key value, you can write a VBA function which takes the primary key value and scans the columns for the specific condition you are looking for.
In short, there is no good SQL-only solution that I can think of.

You said "orders over 1000", which should exclude Pjt_Id = 1 from the output. If you actually want orders at least 1000, change > to >= in this query.
SELECT
p.Pjt_Id,
p.OrderPriceQ1,
p.OrderPriceQ2,
p.OrderPriceQ3,
p.Customer
FROM TblPureferret AS p
WHERE
(p.OrderPriceQ1 > 1000 AND p.OrderPriceQ2 > 1000)
OR (p.OrderPriceQ2 > 1000 AND p.OrderPriceQ3 > 1000);

Try this
select
Pjt_Id ,OrderPriceQ1 ,OrderPriceQ2 ,OrderPriceQ3 ,Customer
from
table
where
(
(OrderPriceQ1>=1000 and OrderPriceQ2 >=1000) or
(OrderPriceQ1>=1000 and OrderPriceQ3 >=1000) or
(OrderPriceQ2>=1000 and OrderPriceQ3 >=1000)
)

Related

Merge SQL Rows in Subquery

I am trying to work with two tables on BigQuery. From table1 I want to find the accession ID of all records that are "World", and then from each of those accession numbers I want to create a column with every name in a separate row. Unfortunately, when I run this:
Select name
From `table2`
Where acc IN (Select acc
From `table1`
WHERE source = 'World')
Instead of getting something like this:
Acc1
Acc2
Acc3
Jeff
Jeff
Ted
Chris
Ted
Blake
Rob
Jack
Jack
I get something more like this:
row
name
1
Jeff
2
Chris
3
Rob
4
Jack
5
Jeff
6
Jack
7
Ted
8
Blake
Ultimately, I am hoping to download the data and somehow use python or something to take each name and count the number of times it shows up with each other name at a given accession number, and furthermore measure the degree to which each pairing is also found with third names in any given column, i.e. the degree to which they share a cohort. So I need to preserve the groupings which exist with each accession number, but I am struggling to find info on how one might do this.
Could anybody point me in the right direct for this, or otherwise is the way I am going about this wise if that is my end goal?
Thanks!
This is not a direct answer to the question you asked. In general, it is easier to handle multiple rows rather than multiple columns.
So, I would recommend that you put each acc value in a separate row and then list the names as an array:
select t2.acc, array_agg(t2.name order by t2.name) as names
from `table2` t2
where t2.acc in (Select t1.acc
From `table1` t1
where t1.source = 'World'
)
group by t2.acc;
Otherwise, you are going to have a challenge just naming the columns in your result set.

Case statement logic and substring

Say I have the following data:
Passes
ID | Pass_code
-----------------
100 | 2xBronze
101 | 1xGold
102 | 1xSilver
103 | 2xSteel
Passengers
ID | Passengers
-----------------
100 | 2
101 | 5
102 | 1
103 | 3
I want to count then create a ticket in the output of:
ID 100 | 2 pass (bronze)
ID 101 | 5 pass (because it is gold, we count all passengers)
ID 102 | 1 pass (silver)
ID 103 | 2 pass (steel)
I was thinking something like the code below however, I am unsure how to finish my case statement. I want to substring pass_code so that we get show pass numbers e.g '2xBronze' should give me 2. Then for ID 103, we have 2 passes and 3 customers so we should output 2.
Also, is there a way to firstly find '2xbronze' if the pass_code contained lots of other things such as '101001, 1xbronze, FirstClass' - this may change so i don't want to substring, could we search for '2xbronze' and then pull out the 2??
SELECT
CASE
WHEN Passes.pass_code like '%gold%' THEN Passengers.passengers
WHEN Passes.pass_code like '%steel%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%bronze%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%silver%' THEN SUBSTRING(passes.pass_code, 1,1)
else 0 end as no,
Passes.ID,
Passes.Pass_code,
Passengers.Passengers
FROM Passes
JOIN Passengers ON Passes.ID = Passengers.ID
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=db698e8562546ae7658270e0ec26ca54
So assuming you are indeed using Oracle (as your DB fiddle implies).
You can do some string magic with finding position of a splitter character (in your case the x), then substringing based on that. Obviously this has it's problems, and x is a bad character seperator as well.. but based on your current set.
WITH PASSCODESPLIT AS
(
SELECT PASSES.ID,
TO_Number(SUBSTR(PASSES.PASS_CODE, 0, (INSTR(PASSES.PASS_CODE, 'x')) - 1)) AS NrOfPasses,
SUBSTR(PASSES.PASS_CODE, (INSTR(PASSES.PASS_CODE, 'x')) + 1) AS PassType
FROM Passes
)
SELECT
PASSCODESPLIT.ID,
CASE
WHEN PASSCODESPLIT.PassType = 'gold' THEN Passengers.Passengers
ELSE PASSCODESPLIT.NrOfPasses
END AS NrOfPasses,
PASSCODESPLIT.PassType,
Passengers.Passengers
FROM PASSCODESPLIT
INNER JOIN Passengers ON PASSCODESPLIT.ID = Passengers.ID
ORDER BY PASSCODESPLIT.ID ASC
Gives the result of:
ID NROFPASSES PASSTYPE PASSENGERS
100 2 bronze 2
101 5 gold 5
102 1 silver 1
103 2 steel 3
As can also be seen in this fiddle
But I would strongly advise you to fix your table design. Having multiple attributes in the same column leads to troubles like these. And the more variables/variations you start storing, the more 'magic' you need to keep doing.
In this particular example i see no reason why you don't simply have the 3 columns in Passes, also giving you the opportunity to add new columns going forward. I.e. to keep track of First class.
You can extract the numbers using regexp_substr(). So I think this does what you want:
SELECT (CASE WHEN p.pass_code LIKE '%gold%'
THEN TO_NUMBER(REGEXP_SUBSTR(p.pass_code, '^[0-9]+'))
ELSE pp.passengers
END) as num,
p.ID, p.Pass_code, pp.Passengers
FROM Passes p JOIN
Passengers pp
ON p.ID = pp.ID;
Here is a db<>fiddle.
This converts the leading digits in the code to a number. Also note the use of table aliases to simplify the query.

SQL Server query related to grouping and counting for inventory analysis

My table is concerned with inventory items, stocking levels and required numbers. An example is given below:
Part Sub Part Pieces Required Pieces In Stock
Barbie Legs 2 1000
Barbie Arms 2 5000
Barbie Head 1 20
Barbie Torso 1 40000
Dora Legs 2 1000
Dora Arms 2 5000
Dora Head 1 0
Dora Torso 1 40000
I want my end result to look like:
Part No: of dolls that can be built
Barbie 20
Dora 0
So the logic is we need a minimum number of each part to make a complete doll. If even one of the required parts is not in stock, then no doll can be made. The complexity comes when we need 2 of certain parts and only 1 of other parts.
How do I achieve this using SQL Server?
Thank you in advance for your help and time.
SELECT part, MIN([Pieces In Stock]/[Pieces Required]) AS PossibleCompleteDolls
FROM tblName
WHERE [Pieces Required] <> 0
GROUP BY part;
The WHERE clause here is just to prevent division by zero.
First add a column of how many can be build with each piece
SELECT *, CAST([Pieces In Stock]/[Pieces Required] as INTEGER) AS canmake
FROM TABLE
Then group that by the part and take the min
SELECT Part, min(canmake) as [No: of dolls that can be built]
FROM (
SELECT *, CAST([Pieces In Stock]/[Pieces Required] as INTEGER) AS canmake
FROM TABLE
WHERE [Pieces Required] != 0
) T
GROUP BY Part

Rename Duplicates in Access with SQL

I have a table in Access, and there is one column of data that has duplicates. I need to rename these duplicates (preferably by appending a 1, 2, 3, etc. at the end). For example, assume my data table looks like so:
ID Name Title
1 George Washington PRES
2 Martha Washington FL
3 John Adams PRES
4 Thomas Jefferson PRES
5 Benjamin Franklin NA
I want to make it look like:
ID Name Title
1 George Washington PRES-1
2 Martha Washington FL-1
3 John Adams PRES-2
4 Thomas Jefferson PRES-3
5 Benjamin Franklin NA-1
I wish to accomplish with an SQL Query in Access, but I'm open to other options. I just need to stay within Access. I'm very new to SQL, but open to learning anything!
Access UPDATE queries which incorporate a subquery can trigger
error #3073 ("Operation must use an updatable query").
When that happens, you can try a "domain function" instead of the subquery to get an updatable query. In your case, DCount is the appropriate domain function.
I tested this query with your sample data in Access 2010 and it did what you requested:
UPDATE YourTable AS y
SET y.Title =
[y].[Title]
& '-'
& DCount("*", "YourTable", "[Title]='" & [y].[Title] & "' AND ID <= " & [y].[ID]);
Note you must replace YourTable with your table name in two places in that query.
Also note the basic concept is similar to Gordon's answer. But it's adapted for Access.
However be aware that DCount and the other Access domain functions (DSum, DMin, DMax, DAvg, etc.) are not portable to other databases.
In Access, I think you can do this with a correlated subquery in an update:
update table as t
set title = title & '-' & (select count(*)
from table as t2
where t2.title = t.title and t2.id <= t.id
);

Multiple entries with the same reference in a table with SQL

In a unique table, I have multiple lines with the same reference information (ID). For the same day, customers had drink and the Appreciation is either 1 (yes) or 0 (no).
Table
ID DAY Drink Appreciation
1 1 Coffee 1
1 1 Tea 0
1 1 Soda 1
2 1 Coffee 1
2 1 Tea 1
3 1 Coffee 0
3 1 Tea 0
3 1 Iced Tea 1
I first tried to see who appreciated a certain drink, which is obviously very simple
Select ID, max(appreciation)
from table
where (day=1 and drink='coffee' and appreciation=1)
or (day=1 and drink='tea' and appreciation=1)
Since I am not even interested in the drink, I used max to remove duplicates and keep only the lane with the highest appreciation.
But what I want to do now is to see who in fact appreciated every drink they had. Again, I am not interested in every lane in the end, but only the ID and the appreciation. How can I modify my where to have it done on every single ID? Adding the ID in the condition is also not and option. I tried switching or for and, but it doesn't return any value. How could I do this?
This should do the trick:
SELECT ID
FROM table
WHERE DRINK IN ('coffee','tea') -- or whatever else filter you want.
group by ID
HAVING MIN(appreciation) > 0
What it does is:
It looks for the minimum appreciation and see to it that that is bigger than 0 for all lines in the group. And the group is the ID, as defined in the group by clause.
as you can see i'm using the having clause, because you can't have aggregate functions in the where section.
Of course you can join other tables into the query as you like. Just be carefull not to add some unwanted filter by joining, which might reduce your dataset in this query.