How to make one column fixed? - sql

There is one scheme and different items inside it, so the scenario is that if user send SchemeID to the procedure then it should return the SchemeName(once) and all items inside a scheme i.e. DescriptionOfitem, Quantity, Rate, Amount... in this format
SchemeName DescriptionOfItems Quantity Unit Rate Amount
Scheme01 Bulbs 2 M2 200 400
Titles 10 M3 300 3000
SolarPanels 2 M2 1000 2000
Bricks 50 M9 50 2500
Total 7900
My try, it works but it also repeats the SchemeName for each row and can't find total
Select
Schemes.SchemeName,
ContractorsWorkDetails.ContractorsWorkDetailsItemDescription,
ContractorsWorkDetails.ContractorsWorkDetailsUnit,
ContractorsWorkDetails.ContractorsWorkDetailsItemQuantity,
ontractorsWorkDetails.ContractorsWorkDetailsItemRate,
ContractorsWorkDetails.ContractorsWorkDetailsAmount
From ContractorsWorkDetails
Inner Join Schemes
ON Schemes.pk_Schemes_SchemeID= ContractorsWorkDetails.fk_Schemes_ContractorsWorkDetails_SchemeID
Where ContractorsWorkDetails.fk_Schemes_ContractorsWorkDetails_SchemeID= 2
Update:
I tested the query as suggested below but it gives this kinda result

You can get the total using grouping sets. I would advise you to keep the schema name on each row. If you want it filtered out on certain rows, then do that at the application layer.
Now, having said that, I think this will do what you want in SQL:
Select (case when GROUPING(cwd.ContractorsWorkDetailsItemDescription) = 0
then 'Total'
when row_number() over (partition by s.SchemeName
order by cwd.ContractorsWorkDetailsItemDescription
) = 1
then s.SchemeName else ''
end) as SchemeName,
cwd.ContractorsWorkDetailsItemDescription,
cwd.ContractorsWorkDetailsUnit,
cwd.ContractorsWorkDetailsItemQuantity,
cwd.ContractorsWorkDetailsItemRate,
SUM(cwd.ContractorsWorkDetailsAmount) as ContractorsWorkDetailsAmount
From ContractorsWorkDetails cwd Inner Join
Schemes s
ON s.pk_Schemes_SchemeID = cwd.fk_Schemes_ContractorsWorkDetails_SchemeID
Where cwd.fk_Schemes_ContractorsWorkDetails_SchemeID = 2
group by GROUPING SETS ((s.SchemeName,
cwd.ContractorsWorkDetailsItemDescription,
cwd.ContractorsWorkDetailsUnit,
cwd.ContractorsWorkDetailsItemQuantity,
cwd.ContractorsWorkDetailsItemRate
), s.SchemeName)
Order By GROUPING(cwd.ContractorsWorkDetailsItemDescription),
s.SchemeName, cwd.ContractorsWorkDetailsItemDescription;
The reason you don't want to do this in SQL is because the result set no longer has a relational structure: the ordering of the rows is important.

Related

SQL : how to distinguish between different rows with same value in some field and have a separate function applied to another field

I have a query output showing a list of orders. Some orders might occupy more then one record in the query output if those orders consist of sub-orders.Each sub-order occupies a separate line in the output. There is the OrderID column which has the same value for all sub-orders in the output:
OrderID Sub-Order Price
1 1 100
1 2 50
2 1 30
3 1 50
I need to add a column "Discount" to the output and fill it by following rules:
If certain order has one sub-order - the discount is 10% of the Price
If certain order has more than one sub-order, the discount is 20% on all sub-orders'
My query is a UNION of two SELECTs.
I use mssql with ms sql studio
Use CASE and COUNT window function
SELECT OrderID, Sub-Order, Price,
CASE WHEN (count(*) OVER (PARTITION BY OrderID)) > 1
THEN Price * 0.8
ELSE Price * 0.9
END
FROM ( table or <query> )

SQL: Select Top 2 Query is Excluding Records with more than 2 Records

I just joined after having a problem writing a query in MS Access. I am trying to write a query that will pull out the first two valid samples in from a list of replicated sample results and then would like to average the sample values. I have written a query that does pull samples with only two valid samples and averages these values. However, my query doesn't pull samples where there are more than two valid sample results. Here's my query:
SELECT temp_platevalid_table.samp_name AS samp_name, avg (temp_platevalid_table.mean_conc) AS fin_avg, count(temp_platevalid_table.samp_valid) AS sample_count
FROM Temp_PlateValid_table
WHERE (Temp_PlateValid_table.id In (SELECT TOP 2 S.id
FROM Temp_PlateValid_table as S
WHERE S.samp_name = S.samp_name and s.samp_valid=1 and S.samp_valid=1
ORDER BY ID))
GROUP BY Temp_PlateValid_table.samp_name
HAVING ((Count(Temp_PlateValid_table.samp_valid))=2)
ORDER BY Temp_PlateValid_table.samp_name;
Here's an example of what I'm trying to do:
ID Samp_Name Samp_Valid Mean_Conc
1 54d2d2 1 15
2 54d2d2 1 20
3 54d2d2 1 25
The average mean_conc should be 17.5, however, with my current query, I wouldn't receive a value at all for 54d2d2. Is there a way to tweak my query so that I get a value for samples that have more than two valid values? Please note that I'm using MS Access, so I don't think I can use fancier SQL code (partition by, etc.).
Thanks in advance for your help!
Is this what you want?
select pv.samp_name, avg(pv.value_conc)
from Temp_PlateValid_table pv
where pv.samp_valid = 1 and
pv.id in (select top 2 id
from Temp_PlateValid_table as pv2
where pv2.samp_name = pv.samp_name and pv2.samp_valid = 1
)
group by pv.samp_name;
You might need avg(pv.value_conc * 1.0).

Join Tables to find SUM for Points available and points completed

I have three tables: Achievements, Characters, and Character_Achievements table that store's the ID's of completed achievements and user id. I am looking to get each category, total amount of points possible and also the amount completed.
I am able to get each category and the amount of points possible but I am unsuccessful at retrieving the completed count as well.
I currently use this to get each category and the amount of points possible
SELECT achievements.category, SUM(points) AS Total
FROM achievements
GROUP BY achievements.category ORDER BY achievements._id asc
I get these results.
Category Total
Operations 50
Events 25
I can also get the amount of points completed
SELECT achievements.category, SUM(points) AS Completed
FROM achievements
LEFT JOIN character_achievements
ON character_achievements.achievements_id = achievements._id
LEFT JOIN character
ON character_achievements.character_id = character._id
WHERE character._id = '1'
which returns this but only the categories that are completed. How do I combine these two queries together.
Category Completed
Operations 50
Events 25
I've tried UNION but it does not return the results I need.
Here are my example tables
Achievements Table
Category Title Points
Operations Epic Enemies 25
Operations Explosive Conflict 25
Events Bounty Contract 25
Character_Achievements Table
Character Character_id Achievements_id
Operations 1 1
Events 1 3
The results I'm looking for would like this.
Results
Category Completed Total
Operations 25 50
Events 25 25
I am able
If I'm understanding your question correctly, you can use SUM with CASE:
SELECT a.category,
SUM(CASE WHEN ca.achievements_id is not null then points end) AS Completed,
SUM(points) Total
FROM achievements a
LEFT JOIN character_achievements ca
ON ca.achievements_id = a._id
GROUP BY a.category
ORDER BY a._id asc

Using "order by" and fetch inside a union in SQL on as400 database

Let's say I have this table
Table name: Traffic
Seq. Type Amount
1 in 10
2 out 30
3 in 50
4 out 70
What I need is to get the previous smaller and next larger amount of a value. So, if I have 40 as a value, I will get...
Table name: Traffic
Seq. Type Amount
2 out 30
3 in 50
I already tried doing it with MYSQL and quite satisfied with the results
(select * from Traffic where
Amount < 40 order by Amount desc limit 1)
union
(select * from Traffic where
Amount > 40 order by Amount desc limit 1)
The problem lies when I try to convert it to a SQL statement acceptable by AS400. It appears that the order by and fetch function (AS400 doesn't have a limit function so we use fetch, or does it?) is not allowed inside the select statement when I use it with a union. I always get a keyword not expected error. Here is my statement;
(select seq as sequence, type as status, amount as price from Traffic where
Amount < 40 order by price asc fetch first 1 rows only)
union
(select seq as sequence, type as status, amount as price from Traffic where
Amount > 40 order by price asc fetch first 1 rows only)
Can anyone please tell me what's wrong and how it should be? Also, please share if you know other ways to achieve my desired result.
How about a CTE? From memory (no machine to test with):
with
less as (select * from traffic where amount < 40),
more as (select * from traffic where amount > 40)
select * from traffic
where id = (select id from less where amount = (select max(amount from less)))
or id = (select id from more where amount = (select min(amount from more)))
I looked at this question from possibly another point of view. I have seen other questions about date-time ranges between rows, and I thought perhaps what you might be trying to do is establish what range some value might fall in.
If working with these ranges will be a recurring theme, then you might want to create a view for it.
create or replace view traffic_ranges as
with sorted as
( select t.*
, smallint(row_number() over (order by amount)) as pos
from traffic t
)
select b.pos range_seq
, b.id beg_id
, e.id end_id
, b.typ beg_type
, e.typ end_type
, b.amount beg_amt
, e.amount end_amt
from sorted b
join sorted e on e.pos = b.pos+1
;
Once you have this view, it becomes very simple to get your answer:
select *
from traffic_ranges
where 40 is between beg_amt and end_amt
Or to get only one range where the search amount happens to be an amount in your base table, you would want to pick whether to include the beginning value or ending value as part of the range, and exclude the other:
where beg_amt < 40 and end_amt >= 40
One advantage of this approach is performance. If you are finding the range for multiple values, such as a column in a table or query, then having the range view should give you significantly better performance than a query where you must aggregate all the records that are more or less than each search value.
Here's my query using CTE and union inspired by Buck Calabro's answer. Credits go to him and WarrenT for being SQL geniuses!
I won't be accepting my own answer. That will be unfair. hehe
with
apple(seq, type, amount) as (select seq, type, amount from traffic where amount < 40
order by amount desc fetch first 1 rows only),
banana(seq, type, amount) as (select seq, type, amount from traffic where
amount > 40 fetch first 1 rows only)
select * from apple
union
select * from banana
It's a bit slow but I can accept that since I'll only use it once in the progam.
This is just a sample. The actual query is a bit different.

Different SQL selection based on inter-record condition

I have a table that holds allocations of problem reports (PRs) as follows:
TABLE "ALLOCATIONS"
ALLOCATIONID PRID DATEALLOCATED ENG_ID
1 401 20-SEP-06 10.48.00 1
2 401 20-SEP-06 10.48.00 2
3 401 20-SEP-06 10.48.00 2
4 402 20-SEP-06 12.35.00 1
5 402 20-SEP-06 12.43.00 1
6 402 20-SEP-06 13.43.00 2
7 700 14-OCT-12 13.30.05 1
8 700 14-OCT-12 13.30.35 2
9 700 14-OCT-12 14.30.35 2
The most recent allocation determines which engineer the PR is now assigned to. I want to find all the PRs that are assigned to engineer 2 for example.
So I look for the most recent allocation for each PRID, check the ENG_ID, then pull out the information from this table if the ENG_ID is correct.
This table contains the actual PR descriptions (and other info omitted here for clarity).
TABLE "PROBLEMS"
PRID TITLE
401 Something
402 Something
700 Something
To do this I have used the DATEALLOCATED field as follows:
SELECT PRID, TITLE FROM PROBLEMS p WHERE p.PRID IN
(
SELECT GROUPEDALLOC.PRID FROM allocations alloc INNER JOIN
(
SELECT PRID, MAX(DATEALLOCATED) AS MaxAllocationDate
FROM allocations
GROUP BY PRID
)
groupedAlloc ON alloc.PRID = groupedAlloc.PRID
AND ALLOC.DATEALLOCATED = groupedAlloc.MaxAllocationDate
AND ENG_ID = 2
)
ORDER BY PRID DESC;
Now this works fine for records 7,8,9 above which were inserted with a long date format that includes the seconds, however for the older records which didn't log the seconds this will obviously not work. For these records I want to fall back on the allocationID (which may or may not be sequential obviously - however it is a last resort and better than nothing).
My question is, how do I modify my query to perform this extra condition on the DATEALLOCATED (i just want to see if they are all equal for a particular PRID), and then use the ALLOCATIONID instead?
I am using OracleXE but I want to stick to standard SQL if possible.
Does this do it for you ?
WITH
BY_DATE
AS (SELECT PRID, MAX (DATEALLOCATED) AS MAXDATE FROM ALLOCATIONS GROUP BY PRID),
BY_ALLOC
AS (SELECT A.PRID, MAXDATE, MAX (ALLOCATIONID) AS MAXALLOC
FROM ALLOCATIONS A JOIN BY_DATE B ON
A.PRID = B.PRID AND
A.DATEALLOCATED = B.MAXDATE
GROUP BY A.PRID, MAXDATE)
SELECT A.PRID, A.ENG_ID
FROM ALLOCATIONS A JOIN BY_ALLOC B ON
A.ALLOCATIONID = B.MAXALLOC;