SQLite How to randomly select minimum values for all cases? - sqlite

I want to randomly select a row from a table where multiple minimum values can exist in the column Number. For example I have a Table containing Titles, Numbers and Categories like so:
Ti Nu Ca
A 0 c7
W 0 c7
Y 0 c7
C 0 c9
H 3 c9
This query will return a random row where Number equals 0 AND Ca equals c7:
SELECT * FROM Table WHERE ((Ca = 'c7') And Nu = ( SELECT min(Nu) FROM Table )) ORDER BY RANDOM() LIMIT 1;
But when the Table contains:
Ti Nu Ca
A 3 c7
W 1 c7
Y 5 c7
C 0 c9
H 3 c9
The above query does not return anything. I would expect the row "W 1 c7" being returned. What am I doing wrong?

Your sub-query looks like it was always matching Nu = 0 regardless of what Ti was, and since there is no row where Nu = 0 and Ca = "c7", it returns nothing. You'll probably need something like this:
SELECT * FROM [Table] x
WHERE Ca = 'c7' And Nu = (SELECT min(Nu) FROM [Table] where x.Ca = Ca)
ORDER BY RANDOM() LIMIT 1;
If you add another row containing Ti="Z", Nu=1, Ca="c7" then you should see the Ti value flip between "W" and "Z" in the returned row.

Related

Convert answer to percentage with two decimals place SQL

I’m trying to figure out how to convert the Male Percentage column to a percentage with decimals to the hundredths
select Top 20 pa.State,
Sum(case when p.gender='M' then 1 else 0 end) as [Male Count],
Sum(case when p.gender='F' then 1 else 0 end) as [ Female Count],
100*sum(case when gender='m' then 1 else 0 end )/count(*) as [Male Percentage]
From [dbo].[Patients] as p
Join PatientAddresses as pa
on p.mrn=pa.MRN
group by pa.State
The results I got.
State
Male Count
Female Count
Male Percentage
UT
105
120
46
NC
1152
1123
50
WI
700
669
51
MA
1486
1424
51
SQL Server by default will report integer division as integer numbers. If you want to force two decimal places, use ROUND(x, 2), where x is a float. One way to make x a float here is to multiply the percentage by the float 100.0 instead of the integer 100.
SELECT TOP 20
pa.State,
COUNT(CASE WHEN p.gender = 'M' THEN 1 END) AS [Male Count],
COUNT(CASE WHEN p.gender = 'F' THEN 1 END) AS [Female Count],
ROUND(100.0*COUNT(CASE WHEN gender = 'm' THEN 1 END) / COUNT(*), 2) AS [Male Percentage]
FROM [dbo].[Patients] AS p
INNER JOIN PatientAddresses AS pa
ON p.mrn = pa.MRN
GROUP BY
pa.State;
Side note: Using TOP without ORDER BY does not make much sense, because it is not clear which 20 records you want to see. So, adding an ORDER BY clause here is probably what you want, unless you are OK with getting back 20 random states.
Edit:
If you want to view the output in SSMS with only two decimal places, and not just with a precision of 2 decimal places, then use CONVERT:
CONVERT(DECIMAL(10,2), 100.0*COUNT(CASE WHEN gender = 'm' THEN 1 END) / COUNT(*))

Display result according to the age groups that are dynamically

I want to display data according to the values like if member age is 18 it falls in the age group of 11-20 if age is 27 it falls in 21-30 if age is 16 it falls in 11-20 etc Remember that all age groups are dynamically here is the code `BEGIN
INSERT INTO #temp3
SELECT items from Split(#ageGroup,';')
INSERT INTO #FacilityWiseAges
Select um.ID as memberId, um.groups as groups from (Select * from #temp3,udv_Members )as um
WHERE DATEDIFF(MONTH,DOB,GETDATE())/12 <= SUBSTRING(#ldata, 0, CHARINDEX('-',#ldata))
OR DATEDIFF(MONTH,DOB,GETDATE())/12 >= SUBSTRING(#ldata, CHARINDEX('-',#ldata) + 1, LEN(#ldata))
FETCH NEXT FROM detail
INTO
#ldata
END
CLOSE detail;
DEALLOCATE detail;
Select Distinct um.MemberName,
um.ID,
um.BillingModeId,
um.BillingModeName,
um.MembershipTypeID,
um.MembershipType,
DATEDIFF(MONTH,DOB,GETDATE())/12 as MemberAge,
SUBSTRING(fac.Name,CHARINDEX('-',fac.Name + '-')+1,LEN(fac.Name)) as FacilityName,
fac.MinimumAge as FacilityAgeFrom,
fac.MaximumAge as FacilityAgeTo,
Convert(nvarchar(max),fac.MinimumAge) + ' - ' + Convert(nvarchar(max),fac.MaximumAge) as Default_FacilityAgeGroup,fages.groups
from udv_Members um
LEFT JOIN #FacilityWiseAges fages on fages.memberID = um.ID
INNER JOIN [dbo].[tbl_Bills] bill on bill.MemberID = um.ID
INNER JOIN [dbo].[udv_MembershipTypes] mt on mt.ID = um.MembershipTypeID
INNER JOIN [dbo].[tbl_BillingModes] AS bm ON bm.ID = [dbo].[udf_BillingModeByMembershipTypeID] (mt.ID)
INNER JOIN [dbo].[tbl_Bills_FacilityContractDetails] as fcb on fcb.BillID = bill.ID
INNER JOIN [dbo].[tbl_Facilities_Contract_Details] as fcd on fcd.ID = fcb.ContractDetailID
INNER JOIN [dbo].[tbl_Facilities_Branches_Durations_Rates] fc on fc.ID = fcd.RateID
INNER JOIN [dbo].tbl_Facilities_Branches_Durations as fdc on fdc.ID=fc.FacilityBranchGenderDurationID
INNER JOIN [dbo].[tbl_Facilities_Branches_Genders] gb on gb.ID = fdc.FacilityBranchGenderID
INNER JOIN [dbo].[tbl_Facilities_Branches] AS bab ON bab.ID =gb.FacilityBranchID
INNER JOIN [dbo].[udv_Facilities] AS fac ON fac.ID = bab.FacilityID
INNER JOIN [dbo].[tbl_Member_Ledger] ledger ON ledger.MemberLedgerId = bill.MemberLedgerID
INNER JOIN dbo.tbl_TransactionType AS t ON t.ID = ledger.TransactionTypeID
Drop table #temp3
Drop table #FacilityWiseAges`
I assume by tagging asp.net, you are doing an sqlconnection sqlcommand.executereder call.
After doing the sqlcommand.executereader call, save the datareader which is returned into a gridview.datasource. Then call GridView.Databind() to actually send the command to the server and to receive the returned table.
To solve your question do the following:
Depending on your SQLcommand, the results you get are saved into the gridview like a excel sheet.
The result from your sql server is then interpreted and accessable in the following:
In this example I am using this table:
a b c d e f g
a b c d e f g
a b c d e f g
a b c d e f g
So, we have 4 rows and 7 columns.
With GridView.Rows(index of row).Cells(index of column).Text we can fetch the cell data as string. Example: GridView.Rows(0).Cells(2).Text returns c (Rows 1, Column 3). GridView.Rows(1).Cells(4).Text returns e (Row 2, Column 5).
Your select column order specifies the order of the columns. Knowing that, you can ask for the age. If we assume that a stands for the age, we can do:
If CInt(GridView.Row(0).Cells(0).Text) > 11 and CInt(GridView.Row(0).Cells(0).Text) < 20 Then
//your code
end if
Continue the if statement for the other ages and specify your special code there.
Regards,
Niru

How to calculate a row value based on the previous row value in the same column

I have the following data set:
DATE CODE RANK PARTITION
? ABS 0 1
12/04/2014 RET 1 1
20/04/2014 RET 2 1
01/05/2014 ABS 2 1
13/05/2014 RET 2 1
01/06/2015 ABS 2 1
09/10/2015 RETk 2 1
? ABS 0 2
02/04/2015 RET 1 2
03/04/2015 RET 2 2
04/04/2015 ABS 2 2
05/04/2015 STT 3 2
06/04/2015 RETk 4 2
07/04/2015 RETk 4 2
RANK is the column I want to calculate in my SQL given the columns DATE, CODE AND the previous value of the same column. It's initialized here to 0.
The logic I want to implement is as follows:
If RANK-1 (previous row) IS NULL AND CODE = ABS THEN RANK = 0
If RANK-1 (previous row) IS NULL AND CODE <> ABS THEN RANK <- (RANK-1) + 1
If RANK-1 = 0 or 1 AND CODE = RET THEN RANK <- (RANK-1) + 1
If RANK-1 = 2 AND CODE = STT THEN RANK <- (RANK-1) + 1
If RANK-1 = 3 AND CODE = RETk THEN RANK <- (RANK-1) + 1
If CODE = ABS THEN RANK <- (RANK-1) (previous row)
Else 0
The Teradata release I am using is R14. The calculation is done on a partition basis as shown in the example above. I have added some more constraints in the model to make it clearer. In this example, if the current code is RET, I do not increase the rank until the previous one is 0 or 1. Similarly, If my current code is RETk, I do not increase the rank until the previous one is equal to 3, otherwise, I do not change the rank. I repeat the same process in the following partition and so on ...
I cannot figure out how to update the current column value given the previous one... I tried many logic implementation with OLAP functions without success.
Can anyone give me a hint?
Thank you very much for your help
You can always use a recursive query for tasks like this. But performance will be bad unless the number of rows per group is low.
First you need a way to advance to the next row, as the next row's date can't be calculated based on the current row's date you must materialize the data and add a ROW_NUMBER:
CREATE TABLE tab(dt DATE, CODE VARCHAR(10), rnk INT, part INT);
INSERT INTO tab( NULL,'ABS' ,0 , 1);
INSERT INTO tab(DATE'2014-04-12','RET' ,1 , 1);
INSERT INTO tab(DATE'2014-04-20','RET' ,2 , 1);
INSERT INTO tab(DATE'2014-05-01','ABS' ,2 , 1);
INSERT INTO tab(DATE'2014-05-13','RET' ,2 , 1);
INSERT INTO tab(DATE'2014-06-01','ABS' ,2 , 1);
INSERT INTO tab(DATE'2014-10-09','RETk',2 , 1);
INSERT INTO tab( NULL,'ABS' ,0 , 2);
INSERT INTO tab(DATE'2015-04-02','RET' ,1 , 2);
INSERT INTO tab(DATE'2015-04-03','RET' ,2 , 2);
INSERT INTO tab(DATE'2015-04-04','ABS' ,2 , 2);
INSERT INTO tab(DATE'2015-04-05','STT' ,3 , 2);
INSERT INTO tab(DATE'2015-04-06','RETk',4 , 2);
INSERT INTO tab(DATE'2015-04-07','RETk',4 , 2);
CREATE VOLATILE TABLE vt AS
(
SELECT dt, code, part
-- used to find the next row
,ROW_NUMBER() OVER (PARTITION BY part ORDER BY dt) AS rn
FROM tab
) WITH DATA
PRIMARY INDEX(part, rn)
ON COMMIT PRESERVE ROWS
;
And now it's just applying your logic using CASE row after row:
WITH RECURSIVE cte (dt, code, rnk, part, rn) AS
(
SELECT
dt
,code
,CASE WHEN code = 'ABS' THEN 0 ELSE 1 END
,part
,rn
FROM vt
WHERE rn = 1
UNION ALL
SELECT
vt.dt
,vt.code
,CASE
WHEN cte.rnk IN (0,1) AND vt.CODE = 'RET' THEN cte.rnk + 1
WHEN cte.rnk = 2 AND vt.CODE = 'STT' THEN cte.rnk + 1
WHEN cte.rnk = 3 AND vt.CODE = 'RETk' THEN cte.rnk + 1
WHEN vt.CODE = 'ABS' THEN cte.rnk
ELSE cte.rnk
END
,vt.part
,vt.rn
FROM vt JOIN cte
ON vt.part =cte.part
AND vt.rn =cte.rn + 1
)
SELECT *
FROM cte
ORDER BY part, dt;
But I think your logic is not actually like this (based on the previous rows exact RANK value), you're just stuck in procedural thinking :-)
You might be able to do what you want using OLAP-functions only...
Something along the lines of:
create table table1
(
datecol date,
code varchar(10),
rankcol integer
);
--insert into table1 select '2014/05/13', 'RETj', 0;
select
case
when s1.code='ABS' and s2.rankcol = 1 then 1
when s1.code='RET' and s2.rankcol = 0 then 1
when s1.code='RET' and s2.rankcol = 1 then 2
else 0
end RET_res,
s1.*, s2.*
from
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s1,
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s2
where s1.var1=s2.var1-1
order by s1.var1
;

SQL Query results different on different environments

There are 21 rows in the table. 7 are for year 2011, 7 are for 2012 and the remaining 7 are for 2013. I want to get the latest year's 7 rows.
SELECT S1, S2, S1 * S2 AS M
FROM BLGND
WHERE DID = #dID AND
PMID = #pmID AND
yearr IN (SELECT MAX(yearr) AS y FROM BLGND AS BLGND_1)
I successfully bring the latest year's rows with the query above in MS Visual Studio Environment. But When I run the query in an ASP.NET page It brings all 21 records.
In other words yearr IN (SELECT MAX(yearr) AS y FROM BLGND AS BLGND_1) makes no difference.
yearr column's type is int
What could be the problem? OR is there a better query you know?
You could simply write
SELECT S1, S2, S1 * S2 AS M
FROM BLGND
WHERE DID = #dID AND
PMID = #pmID AND
YEAR([fieldWithDate]) = #yearRequested
Where fieldWithDate is assumed to be a smalldatetime or datetime field from which you want to get the year value and #yearRequested is a parameter that you pass to your query from your code.
You could extract the year value using the YEAR(expression) T-SQL function
Of course, if the field to search for the year value is just an integer containing the year values of your rows the query is simpler
SELECT S1, S2, S1 * S2 AS M
FROM BLGND
WHERE DID = #dID AND
PMID = #pmID AND
fieldWithYearValue = #yearRequested
If you don't want to pass the parameter for the year, but just want to retrieve always the set of rows of the last year for a specific user then
SELECT S1, S2, S1 * S2 AS M
FROM BLGND
WHERE DID = #dID AND
PMID = #pmID AND
fieldWithYearValue = (SELECT MAX(fieldWithYearValue) FROM BLGND)

SQLite Query to group continuous range of numbers into different grouping sets

I want get the islands of this table below:
Group MemberNo
A 100
A 101
A 200
A 201
A 202
A 203
X 100
X 101
A 204
X 301
X 302
A 500
A 600
I want get this results using SQL (the islands):
Group FromMemberNo ToMemberNo
A 100 101
A 200 204
X 100 101
X 301 302
A 500 500
A 600 600
I have seen a lot of codes/forums for this but not working with SQLite because SQLite doesn't have CTEs.
100-101 is continuous so that it will be group into one.
Does anyone know how to do it in SQLite?
The fastest way to do this would be to go through the ordered records of this table in a loop and collect the islands manually.
In pure SQL (as a set-oriented language), this is not so easy.
First, we find out which records are the first in an island. The first record does not have a previous record, i.e., a record with the same group but with a MemberNo one smaller:
SELECT "Group",
MemberNo AS FromMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)
To find the last record of an island, we have to find the record with the largest MemberNo that still belongs to the same island, i.e., has the same group, and where all MemberNos in the island are continuous.
We detect continuous MemberNos by computing the difference between their values in the first and last records.
The last MemberNo of the island with group G and first MemberNo M can be computed like this:
SELECT MAX(MemberNo) AS LastMemberNo
FROM ThisTable AS t3
WHERE t3."Group" = G
AND t3.MemberNo - M + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = G
AND t4.MemberNo BETWEEN M AND t3.MemberNo)
Finally, plug this into the first query:
SELECT "Group",
MemberNo AS FromMemberNo,
(SELECT MAX(MemberNo)
FROM ThisTable AS t3
WHERE t3."Group" = t1."Group"
AND t3.MemberNo - t1.MemberNo + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = t1."Group"
AND t4.MemberNo BETWEEN t1.MemberNo AND t3.MemberNo)
) AS LastMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)

Resources