Picking out pairs from SQL Server - sql-server

I am working on exercise 16 from SQL-EX.com
Find the pairs of PC models having identical speeds and RAM.
As a result, each resulting pair is shown only once, i.e. (i, j) but not (j, i).
Result set: model with higher number, model with lower number, speed, and RAM.
I used the following query
SELECT B.code, B.model AS BM, A.code, A.model, A.speed, A.ram
FROM PC A
JOIN PC B
ON A.speed = B.speed AND A.ram = B.ram
WHERE A.model <> B.model
ORDER BY B.model ASC
How do I retrieve only the pairs where BM is higher than model?

Instead of using <>, use <:
SELECT
a.model,
b.model,
a.speed,
a.ram
FROM PC a
INNER JOIN PC b
ON b.speed = a.speed
AND b.ram = a.ram
AND b.model < a.model

Change this line:
WHERE A.model <> B.model
To this:
WHERE A.model > B.model
You also need to select the correct columns, but getting that WHERE expression right was the hard part.

Related

SQL optimazation

I am working on a database where a total of 788 data is currently stored and continuously increasing with time.
My code is as follows:
SELECT DISTINCT R.remarks, R.payerId, R.payername, R.payeraddress, R.collectorName, R.serialno, OOI.phone_no, CR.cr_no, F.application_no, R.series, R.txndate, R.amount, T.toda_name, B.brand_name, M.motor_no, M.chassis_no,
M.plate_no, F.date_issue, M.year_model, M.body_color, OOI.ice_person_name, OOI.ice_person_address, OOI.ice_person_contact_no, M.motor_id, F.franchise_id, LEFT(R.remarks, 4) AS franchise_no, SUBSTRING(R.remarks,
CHARINDEX('|', R.remarks) + 1, LEN(R.remarks)) AS motor_noremarks
FROM etracs_tayabas.dbo.Receipt AS R INNER JOIN
etracs_tayabas.dbo.ReceiptItem AS RI ON RI.parentid = R.objid INNER JOIN
etracs_tayabas.dbo.IncomeAccount AS IA ON IA.objid = RI.acctid LEFT OUTER JOIN
dbo.vfTA_tblMotor AS M ON M.motor_no = SUBSTRING(R.remarks, CHARINDEX('|', R.remarks) + 1, LEN(R.remarks)) LEFT OUTER JOIN
dbo.vfTA_tblOperatorOtherInfo AS OOI ON OOI.operator_id = R.payerId LEFT OUTER JOIN
dbo.vfTA_tblCertificateOfRegistration AS CR ON CR.motor_id = M.motor_id LEFT OUTER JOIN
dbo.vfTA_tblFranchise AS F ON F.or_id = R.objid LEFT OUTER JOIN
dbo.vfTA_tblTODA AS T ON T.toda_id = M.toda_id LEFT OUTER JOIN
dbo.vfTA_tblReconciledTaxpayer AS RT ON RT.payer_id = R.payerId LEFT OUTER JOIN
dbo.vfTA_tblBrand AS B ON B.brand_id = M.brand_id
WHERE (IA.objid = 'FTFA00000242') AND (F.franchise_id IS NULL) AND (R.voidId IS NULL) AND (R.remarks IS NOT NULL) AND (RT.rtp_id IS NULL)
Everytime I run this code, it always takes me up to 10 minutes long or more to load up all the values. I tried to make a SQL View of this same code but when I run it, the error Execution Timeout always shows.
I want to know:
What is the best optimization method for views and stored procedure?
How can I lessen the time it takes for the data to load given that it has 788 data and increasing over time?
How to prevent a lot of execution timeout to happen in SQL or even in a program?
Some sites that help teaches SQL optimization.
I am trying to learn optimization right now because I noticed that when I make a query, it usually takes a lot of time to load up and sometimes producing the error 'Execution Timeout'
I am currently new with this. Thanks in advance.
I think using DISTINCT against such many columns cost a lot. Is that really necessary for your query?
I also wondered the following part.
LEFT OUTER JOIN dbo.vfTA_tblMotor AS M ON M.motor_no = SUBSTRING(R.remarks, CHARINDEX('|', R.remarks) + 1, LEN(R.remarks))
It means there's no way to use INDEX for this relationship. How about adding a column to table etracs_tayabas.dbo.Receipt where you store the substring result, and calculating it when a record is inserted/updated to the table. In this way, you can make INDEX for this relationship and can optimise that part of JOIN.

Returning from a join the first result of one column based one a second column

I need some help to improve part of my query. The query is returning the correct data, I just need to exclude some extra information that I don't need.
I believe that one of the main parts that will change is:
JOIN TBL_DATA_TYPE_RO_BODY TB ON TB.FK_ID_TBL_FILE_NAMES=VMI.ID_TBL_FILE_NAMES
In this part, I have, for example, 2 FK_ID_TBL_FILE_NAMES, it will return 2 results from TBL_DATA_TYPE_RO_BODY.
The data that I have is (I excluded some extra columns):
If I have 2 or more equal MAG for the same field "ONLY_FIELD_NAME" I should return only the first one (I don't care about the others one). I believe that this is a simple case for Group by, but I am having trouble doing the group by on the join.
My ideas:
Use select top (i.e. here)
Use first valeu (i.e. here)
What I have (note the 2 last lines):
Freq|Mag|Phase|Date|ONLY_FILE_NAME
1608039|767|3234|37:00.0|RO_Mass_Load_4b
1608039|781|3371|44:00.0|RO_Mass_Load_4b
1608039|788|3138|37:00.0|RO_Mass_Load_4b
1608039|797|3326|44:00.0|RO_Mass_Load_4b
1608039|808|3117|37:00.0|RO_Mass_Load_4b
1608039|808|3269|44:00.0|RO_Mass_Load_4b
What I would like to have (note the last line):
Freq|Mag|Phase|Date|ONLY_FILE_NAME
1608039|767|3234|37:00.0|RO_Mass_Load_4b
1608039|781|3371|44:00.0|RO_Mass_Load_4b
1608039|788|3138|37:00.0|RO_Mass_Load_4b
1608039|797|3326|44:00.0|RO_Mass_Load_4b
1608039|808|3117|37:00.0|RO_Mass_Load_4b
Note that the mag field is coming from my JOIN.
Ideas? Any help?
In case you wanna see the whole code is:
SELECT TW.CURRENT_MEASUREMENT as Cycle_Current_Measurement,
TW.REF_MEASUREMENT as Cycle_Ref_Measurement,
CONVERT(REAL,TT.CURRENT_TEMP) as Cycle_Current_Temp,
CONVERT(REAL,TT.REF_TEMP) as Cycle_Ref_Temp,
TP.TYPE as Cycle_Type, TB.FREQUENCY as Freq,
TB.MAGNITUDE as Mag,
TB.PHASE as Phase,
VMI.TIME_FORMATTED as Date,
VMI.ID_TBL_FILE_NAMES as IdFileNames, VMI.ID_TBL_DATA_TYPE_RO_HEADER as IdHeader, VMI.*
FROM VW_MAIN_INFO VMI
JOIN TBL_DATA_TYPE_RO_BODY TB ON TB.FK_ID_TBL_FILE_NAMES=VMI.ID_TBL_FILE_NAMES
LEFT JOIN TBL_POINTS_AND_CYCLES TP ON VMI.ID_TBL_DATA_TYPE_RO_HEADER = TP.FK_ID_TBL_DATA_TYPE_RO_HEADER
LEFT JOIN TBL_POINTS_AND_MEASUREMENT TW ON VMI.ID_TBL_DATA_TYPE_RO_HEADER = TW.FK_ID_TBL_DATA_TYPE_RO_HEADER
LEFT JOIN TBL_POINTS_AND_TEMP TT ON VMI.ID_TBL_DATA_TYPE_RO_HEADER = TT.FK_ID_TBL_DATA_TYPE_RO_HEADER
Try something like this. the partition by is like a group by; it defines groups over which row_number will auto-increment an integer by 1. The order by tells row_number which rows should have a lower number. So in this example, the lowest date will have RID = 1. Then subquery it, and select only those rows which have RID = 1
select *
from (select RID = row_number() over (partition by tb.Magnitude order by vmi.time_formatted)
from ...<rest of your query>) a
where a.RID = 1

Using SQL to find entries that were originally X and later changed to Y

I recently started using SQL for work and don't have much experience of it so I'm sorry if this is a ridiculous question.
I'm looking for an entry that was originally listed as X but was then later changed to Y, I figure that a nested sub query is the way to go but the one I'm trying doesn't seem to use the nested bit.
Here is the code I'm trying
SELECT *
FROM [HOME].[dba].[ARCHIVE]
where FRIE like 'AR8%'
and RESULT = 'X'
and EXISTS(SELECT FRIE, RESULT
FROM [HOME].[dba].[ARCHIVE]
where RESULT = 'Y');
Everything as far as the EXISTS works but afterwards it just ignores the nested query
Your query doesn't have the same WHERE clause in the EXISTS portion. I think this will work for you:
SELECT *
FROM [HOME].[dba].[ARCHIVE]
WHERE FRIE like 'AR8%'
AND RESULT = 'X'
AMD EXISTS(SELECT TOP 1 1
FROM [HOME].[dba].[ARCHIVE]
where FRIE like 'AR8%' AND RESULT = 'Y');
I'd recommend using an INNER JOIN to a subquery rather than using an EXISTS statement. Something like this:
SELECT *
FROM [HOME].[dba].[ARCHIVE] a
INNER JOIN (SELECT FRIE
FROM [HOME].[dba].[ARCHIVE]
WHERE RESULT = 'Y') t1 ON a.FRIE = t1.FRIE
WHERE
FRIE like 'AR8%'
and RESULT = 'X'
That would return all rows from ARCHIVE where they there is a row with the same FRIE with a RESULT of X and a RESULT of Y.
Hopefully that helps.

SQL Server MAX and GROUP BY not playing nicely together

I have a T-SQL query of the form:
SELECT f.fizz_name, b.buzz_version, fu.foo_name
FROM fizz f
INNER JOIN buzz b
ON f.fizz_id = b.fizz_id
INNER JOIN foo fu
ON b.buzz_id = fu.buzz_id
WHERE f.bar LIKE 'YES'
When I run this query I get the following results:
fizz_name buzz_version foo_name
====================================
Gamma 0.3.960 Test
Gamma 0.3.961 Test
Gamma 0.3.960 Test
Gamma 0.3.961 Test
Delta 0.3.2588 Test
Delta 0.3.2589 Test
Delta 0.3.2588 Test
Delta 0.3.2589 Test
Echo 2.2.38 Test
Echo 2.2.38 Test
The problem with this is that it contains a lot of entries that I don't care about. In reality I only care about the largest buzz_version for each fizz instance, in other words:
fizz_name buzz_version foo_name
====================================
Gamma 0.3.961 Test
Delta 0.3.2589 Test
Echo 2.2.38 Test
...because "2.2.38" is the latest/lexiconographically-highest buzz_version for Echo, and same for the other fizzes.
So I am trying to use GROUP BY in concert with MAX to fetch these values like so:
SELECT f.fizz_name, MAX(b.buzz_version), fu.foo_name
FROM fizz f
INNER JOIN buzz b
ON f.fizz_id = b.fizz_id
INNER JOIN foo fu
ON b.buzz_id = fu.buzz_id
WHERE f.bar LIKE 'YES'
GROUP BY b.buzz_version
But that gives me an error:
Column 'fizz.fizz_name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Where am I going wrong, and why?
You are grouping by the aggregate in your query. You need to group by the scalar columns instead. In this case, group by f.fizz_name, fu.foo_name
You want one result row per fizz_name, so you must group by fizz_name. You show MAX(b.buzz_version) with it and must decide which fu.foo_name to show. E.g.:
SELECT f.fizz_name, MAX(b.buzz_version), MAX(fu.foo_name)
FROM fizz f
INNER JOIN buzz b ON f.fizz_id = b.fizz_id
INNER JOIN foo fu ON b.buzz_id = fu.buzz_id
WHERE f.bar LIKE 'YES'
GROUP BY f.fizz_name;

"if, then, else" in SQLite

Without using custom functions, is it possible in SQLite to do the following. I have two tables, which are linked via common id numbers. In the second table, there are two variables. What I would like to do is be able to return a list of results, consisting of: the row id, and NULL if all instances of those two variables (and there may be more than two) are NULL, 1 if they are all 0 and 2 if one or more is 1.
What I have right now is as follows:
SELECT
a.aid,
(SELECT count(*) from W3S19 b WHERE a.aid=b.aid) as num,
(SELECT count(*) FROM W3S19 c WHERE a.aid=c.aid AND H110 IS NULL AND H112 IS NULL) as num_null,
(SELECT count(*) FROM W3S19 d WHERE a.aid=d.aid AND (H110=1 or H112=1)) AS num_yes
FROM W3 a
So what this requires is to step through each result as follows (rough Python pseudocode):
if row['num_yes'] > 0:
out[aid] = 2
elif row['num_null'] == row['num']:
out[aid] = 'NULL'
else:
out[aid] = 1
Is there an easier way? Thanks!
Use CASE...WHEN, e.g.
CASE x WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END
Read more from SQLite syntax manual (go to section "The CASE expression").
There's another way, for numeric values, which might be easier for certain specific cases.
It's based on the fact that boolean values is 1 or 0, "if condition" gives a boolean result:
(this will work only for "or" condition, depends on the usage)
SELECT (w1=TRUE)*r1 + (w2=TRUE)*r2 + ...
of course #evan's answer is the general-purpose, correct answer

Resources