How to select columns of different lengths - sql

I have a table of part numbers along with many of their properties like such:
[Part Number] [Type] [Manager] [Cat. Code] [etc...]
AAA-001 A Dave 123
DDD-008 D Chris 153
BBB-003 B Dave 254
CCC-008 C Dave 153
...
I'm trying to make a list of all the unique values of each property so it looks more like this:
[Type] [Manager] [Cat. Code] [etc...]
A Dave 123
B Chris 153
C 254
D
However whenever I try to do this using SELECT DISTINCT * or the like, it fills up all the columns so that they're the same length as the longest one, filled horizontally according to the original table:
[Type] [Manager] [Cat. Code] [etc...]
A Dave 123
B Dave 254
C Dave 153
D Chris 153
How do I stop this from happening, and just keep unique values of each column, even if they might be different lengths?

I think you've misunderstood what DISTINCT does. It will filter your results so that all rows returned are unique, not each column. Depending which columns are named in your SELECT, you'll get different results. For an example, see http://www.techonthenet.com/sql_server/distinct.php and the section "Example - With Multiple Expressions"
For your case, if you want lists of the unique values in each column, then you'll have to do a separate query on each column, e.g.:
SELECT DISTINCT [Type] FROM [Parts]
SELECT DISTINCT [Manager] FROM [Parts]
...etc
If you then want them side-by-side (as per your sample desired output), you'll just have to copy and paste them into a spreadsheet or something.

Related

Issue with PIVOTING two columns at the same time and one includes a datetime column in SQL Server

I have a table used for attendance tracking, so I can track the arrival time of a person and the date in which they have arrived. I'm now trying to get a report based on that data and have created a view for it below. I have used the PIVOT function to split up the dates and it works without the column highlighted in bold below. The only issue is that I'm trying to PIVOT on a second column called DeviceFirstScannedTime which returns a datetime value, once that gets added the original PIVOT query doesn't group correctly.
Using SQL Server 2012
Current Query for PIVOTING
SELECT *
FROM (SELECT [Code] AS [CB Code], [Lastname], [Firstname], CompanyName AS [Company], DeviceScannedDateOnly, [Code], **DeviceFirstScannedTime**
FROM [dbo].[vLDN23_DailyReportForPivot]) AS SourceTable
PIVOT (Count([Code]) FOR DeviceScannedDateOnly IN ([06/02/2023], [07/02/2023])) AS PivotTable;
What it shows without the DeviceFirstScannedTime column added, shows it will pivot on DeviceScannedDateOnly and Split the dates correctly (it groups the two scans in the original source table and pivots them);
CB Code
Lastname
Firstname
Company
06/02/23
07/02/23
WSSPS24HX6
Smith
Bob
Stan
1
1
With the datetime column added I get the data separated;
CB Code
Lastname
Firstname
Company
DeviceFirstScannedTime
06/02/23
07/02/23
WSSPS24HX6
Smith
Bob
Stan
2023-02-06 10:12:0060
1
0
WSSPS24HX6
Smith
Bob
Stan
2023-02-07 13:87:0000
0
1
Data is now split which isn't ideal for reporting..
What I'm trying to do is this
Firstname
Company
06 Scanned time
06/02/23
07 Scanned Time
07/02/23
Bob
Stan
2023-02-06 10:12:0060
1
2023-02-07 13:87:0000
1
I've removed the first couple of columns to make it more readable but essentially I'm trying to PIVOT on two columns and split the table accordingly all into one line.
Is that possible with a datatime column?

Including variables after filtering selecting only minimum values in SQL?

I am working with a twin dataset and would like to create a table with the Subject ID (Subject) and twin pair ID (twpair) for the twins with the lower (or one of the twins if the values are equal) lifetime total of marijuana use (MJ1a).
A portion of my table looks like this:
Subject
twpair
MJ1a
156
345
10
157
345
7
158
346
20
159
346
3
160
347
4
161
347
4
I'm hoping to create a table with only the twins that have the lower amount of marijuana use which would look like this:
Subject
twpair
MJ1a
157
345
7
159
346
3
161
347
4
This is the SQL code I have so far:
proc sql;
create table one_twin as
select twpair,min(MJ1a) as minUse, Subject
from twins_deviation
group by twpair;
Unfortunately this ends up causing all of the subjects to be remerged back in the dataset. If I don't include the Subject portion I get the correct values for twpair and MJ1a but not the Subject IDs.
How do I filter the dataset to only include those with the minimum values while also including variables of interest like Subject ID? Note that if two pairs of twins have the SAME value I would like to select one but it doesn't matter which I select. Any tips would be extremely appreciated!
This query should give you the desired result.
select a.subject,a.twpair,a.MJ1a from twins_deviation a join (select twpair,min(mj1a) as mj1a from twins_deviation group by twpair)b on a.twpair=b.twpair and a.mj1a=b.mj1a
If your DB supports analytic/window functions ,the same can be accomplished using a rank function ,solution given below.
EDIT1:to handle same values for mj1a
select subject,twpair,mj1a from(select subject,twpair,mj1a ,row_number() over(partition by twpair order by mj1a) as rnk from twins_deviation)out1 where rnk=1;
EDIT2:Updated solution 1 to include only one twin.
select min(subject) as subject,twpair,mj1a from(select a.subject as subject ,a.twpair as twpair,a.MJ1a as MJ1a from twins_deviation a join (select twpair,min(mj1a) as mj1a from twins_deviation group by twpair)b on a.twpair=b.twpair and a.mj1a=b.mj1a)out1 group by twpair,MJ1a;

Finding the difference between two nearly identical SQL rows

I am developing an application and my boss wants to track all changes that have been made to a record throughout its life.
For instance, if I have the following table:
ID Name City Item Version
1 Mike Miami Test box 1
1 Mike Fort Lauderdale Test box 2
1 Mike Sarasota Testing box 3
And I want to see that from version 1 to version 2 the city was changed to Fort Lauderdale, is there a query that will help me do that? I would really like to be able to do this without specifying all the column names individually, because the actual table has 25+ columns and they may change at any time, plus it would be nice if the query could be easily portable to different tables. Ideally my result would look like the following, but I'm willing to accept anything that would help. Thanks in advance!
ColumnName Previous Value New Value
City Miami Fort Lauderdale
Assuming that the columns are all strings (which is rather necessary for your output format), you can do this by unpivoting the data and using lag():
select c.*
from (select t.id,
lag(v.col) over (partition by t.id order by t.version) as prev_val,
v.val
from t cross apply
(values ('Name', name), ('City', city), ('Item', item)
) v(col, val)
) c
where prev_val <> val;
If you have columns that are not strings, then you'll need to convert them to strings in the values clause.
This also assumes that the values are not NULL. That can be handled, but does not seem necessary.

Select items where count in another field matches (not updatable)

Here I am trying to get the record for my products where the # swab location in Main table matches the count of swab locations in swab Table and Users can checked off the Y/N to verify that the description of the locations are correct.
Here is the example of my 2 tables.
tblMainEquipment
Asset_ID EquipmentName Num_SwapLocations Verified
234 Saijimon 2 N
235 Pasquale 3 N
tblMainSwapLocations
Asset_ID Swap_location
234 Particle Cannon
234 RailGun
235 Particle Cannon
I use the following query to count the number of records, i avoided using a having query to combine both tables since it is not updatable.
qryMainSwapLocationCount
SELECT MSL.Asset_ID, Count(Asset_ID) AS [Count]
FROM tblMainSwapLocation AS MSL
GROUP BY MSL.Asset_ID;
This will give me the result of
qryMainSwapLocationCount
Asset_ID count
234 2
234 1
I used the following as a record source for my form to allow users to verify the inputs.
SELECT MEQ.Asset_ID, MEQ.Equipment_Name,MEQ.Num_swapLocations MEQ.Verified
FROM tblMainEquipment AS MEQ, qryMainSwapLocationCount AS MSLC
WHERE (((MEQ.Asset_ID)=[MSLC].[Asset_ID]) AND ((MEQ.Num_SwapLocations)=[MSLC].[Count]);
This result would be
tblMainEquipment
Asset_ID EquipmentName Num_SwapLocations Verified
234 Saijimon 2 N
However this record set is not editable. Is there any reasons for this?
I think you should put your table tblMainEquipment as your recordsource and bring all the fields from that on to your form:
Then insert an unbound textbox (perhaps close to your Num_SwapLocations field for easy comparison):
Then in this new textbox, put the following in the ControlSource:
=DCount("ASSET_ID","tblMainSwapLocations","ASSET_ID=" & [Asset_ID])
Then open your form and it should count the number of records in table tblMainSwapLocations that have the same Asset_ID as the record currently showing:
You'll then be able to update the Verified field in your tblMainEquipment table.

How to specify row names in MS Access 2007

I have a cross tab query and it pulls only the row name if there is data associated with it in the database. For example, if I have three types of musical instruments:
Guitar
Piano
Drums
Other
My results will show up as:
Guitar 1
Drums 2
It doesn't list Piano because there is no ID associated with Piano in the DB. I know I can specify columns in the properties menu, i.e. "1, 2, 3, 4, 5" will put columns in the DB for each, regardless of whether or not there is data to populate them.
I am looking for a similar solution for rows. Any ideas?
Also, I need NULL values to show up as 0.
Here's the actual SQL (forget the instrument example above)
TRANSFORM Count(Research.Patient_ID) AS CountOfPatient_ID
SELECT
Switch(
[Age]<22,"21 and under",
[Age]>=22 And [AGE]<=24,"Between 22 And 24",
[Age]>=25 And [AGE]<=29,"Between 25 And 29",
[Age]>=30 And [AGE]<=34,"30-34",
[Age]>=35 And [AGE]<=39,"35-39",
[Age]>=40 And [AGE]<=44,"40-44",
[Age]>44,"Over 44"
) AS Age_Range
FROM (Research
INNER JOIN (
SELECT ID, DateDiff("yyyy",DOB,Date()) AS AGE FROM Demographics
) AS Demographics ON Research.Patient_ID=Demographics.ID)
INNER JOIN [Letter Status] ON Research.Patient_ID=[Letter Status].Patient_ID
WHERE ((([Letter Status].Letter_Count)=1))
GROUP BY Demographics.AGE, [Letter Status].Letter_Count
PIVOT Research.Site In (1,2,3,4,5,6,7,8,9,10);
In short, I need all of the rows to show up regardless of whether or not there is a value (for some reason the LEFT JOIN isn't working, so if you can, please use my code to form your answer), and I also need to replace NULL values with 0.
Thanks
I believe this has to do with the way you are joining the instruments table to the IDs table. If you use a left outer join from instruments to IDs, Piano should be included. It would be helpful to see your actual tables and queries though, as your question is kind of vague.
What if you union the select with a hard coded select with one value for each age group.
select 1 as Guitar, 1 as Piano, 1 as Drums, 1 as Other
When you do the transform, each row will have a result that is +1 of the result you want.
foo barTmpCount
-------- ------------
Guitar 2
Piano 1
Drums 3
Other 1
You can then do a
select foo, barTmpCount - 1 as barCount from <query>
and get something like this
foo barCount
-------- ---------
Guitar 1
Piano 0
Drums 2
Other 0