SELECT Age, Education, Triglyceride
FROM nhanes
LIMIT 5| Age | Education | Triglyceride |
|---|---|---|
| 20 | Some college or AA degree | 78 |
| 59 | College graduate or above | 136 |
| 58 | College graduate or above | 128 |
| 67 | Some college or AA degree | 55 |
| 47 | College graduate or above | 52 |
Amanda Ng
A typical starting structure when using SQL is SELECT variable FROM table. This query will give a table showing only the selected variables from the table. To select multiple variables, we can use a comma , to separate them. The shortcut for showing all variables is *.
To avoid overwhelming the reader, we can limit the output table to show only the top observations using LIMIT after the FROM clause. For example:
We can also specify to show only distinct rows with DISTINCT in the SELECT rows. However, you are only allowed to distinct on a single variable or DISTINCT *, such as
To show distinct rows across multiple selected variables, we need to perform a GROUP BY, which will be introduced later.
We can also rename the variable name using AS, such as
To define new variables, we also do it within the SELECT clause such as
We can also define a constant variable across rows.
We can also concatenate strings using ||.
To assign value to a new variable based on some conditions, we use CASE WHEN... THEN... ELSE...END.
In this assignment, we transformed the coded Gender column into the text form column Gender_name where we assign the value Male when it is coded as 1, Female when it is coded as 2, and Unknown otherwise.
Similar to dplyr in R, we can also filter to restrict our output to only show observations that satisfy certain conditions using WHERE clause. Note that the WHERE clause always comes after FROM. To build condition statements, we can use
=, <, >, <=, >=, and !=.AND, OR, and NOTIn this example, we show the top 5 observations with age greater than 20.
| SEQN | Age | Gender | Ethnicity | Citizenship | Education | Marital_status | Household_size | Annual_income | Sedentary_min | Height | Weight | Health_insurance | Private_insurance | Cancer | Heart_attack | Stroke | Inc_exercise | Dec_salt | Dec_fat | LDL_Cholesterol | Triglyceride |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 93801 | 59 | 1 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | $100000 and Over | 600 | 71 | 175 | Yes | NA | No | No | No | No | Yes | Yes | 94 | 136 |
| 93823 | 58 | 2 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | Under $20000 | 180 | 64 | 170 | No | NA | No | No | No | Yes | No | Yes | 156 | 128 |
| 93830 | 67 | 1 | non-Hispanic black | Citizen by birth or naturalization | Some college or AA degree | Married | 2 | $75000 to $99999 | 360 | 75 | 252 | Yes | NA | No | No | Yes | Yes | Yes | Yes | 81 | 55 |
| 93840 | 47 | 1 | non-Hispanic Asian | Citizen by birth or naturalization | College graduate or above | Married | 3 | $100000 and Over | 600 | 70 | 172 | Yes | Yes | No | No | No | Yes | No | No | 104 | 52 |
| 93887 | 63 | 1 | non-Hispanic white | Citizen by birth or naturalization | Some college or AA degree | Divorced | 1 | $15000 to $19999 | 9999 | 70 | 170 | Yes | NA | No | Yes | Yes | No | No | No | 95 | 102 |
SEQN Age Gender Ethnicity Citizenship
1 93801 59 1 Other Hispanic Citizen by birth or naturalization
2 93823 58 2 Other Hispanic Citizen by birth or naturalization
3 93830 67 1 non-Hispanic black Citizen by birth or naturalization
4 93840 47 1 non-Hispanic Asian Citizen by birth or naturalization
5 93887 63 1 non-Hispanic white Citizen by birth or naturalization
Education Marital_status Household_size Annual_income
1 College graduate or above Married 2 $100000 and Over
2 College graduate or above Married 2 Under $20000
3 Some college or AA degree Married 2 $75000 to $99999
4 College graduate or above Married 3 $100000 and Over
5 Some college or AA degree Divorced 1 $15000 to $19999
Sedentary_min Height Weight Health_insurance Private_insurance Cancer
1 600 71 175 Yes <NA> No
2 180 64 170 No <NA> No
3 360 75 252 Yes <NA> No
4 600 70 172 Yes Yes No
5 9999 70 170 Yes <NA> No
Heart_attack Stroke Inc_exercise Dec_salt Dec_fat LDL_Cholesterol
1 No No No Yes Yes 94
2 No No Yes No Yes 156
3 No Yes Yes Yes Yes 81
4 No No Yes No No 104
5 Yes Yes No No No 95
Triglyceride
1 136
2 128
3 55
4 52
5 102
SQL provides the LIKE operator to compare a string to a pattern. The pattern is a quoted string and can include these special characters:
_ matches any single character% matches any sequence of zero or more charactersThis outputs all distinct Ethnicity which contains Hispanic in the entry.
Using ORDER BY, we can order the rows in the table based on column(s) of your choice. By default, it orders the row in ascending order (ASC). If you want to show the row corresponding to the larger values as the top row, put DESC after your column name.
This outputs the top 5 distinct Age in descending order, the first row corresponds to the observation with largest age in the dataset.
Sometimes, we are interested in the group level statistical summaries. We can use GROUP BY and aggregation functions to so do.
Here are some common aggregation functions in SQL:
AVG()MAX()MIN()COUNT()SUM()Tips:
COUNT(*) is you want to summarize number of rows in the tableCOUNT(DISTINCT variable) is you want to summarize number of distinct values in a variableFor example, this shows the mean age for each education groups.
Note that, when we use GROUP BY, we must include the grouping column in the SELECT clause. Any columns storing individual-level values should not be included in SELECT after grouping since we are collapsing the dataset into group level observations. In other words, you can only include the grouping column and aggregated measures applied on the grouping column.
You can also group by more than one column, such as:
| Education | Gender | avg_age |
|---|---|---|
| High school graduate/GED or equivalent | 2 | 53.38462 |
| Less than 9th grade | 2 | 59.76190 |
| NA | 1 | 17.52941 |
| Some college or AA degree | 2 | 52.66667 |
| 9-11th grade | 2 | 54.07407 |
| Some college or AA degree | 1 | 51.37500 |
| 9-11th grade | 1 | 52.96154 |
| NA | 2 | 17.35714 |
| Don’t Know | 1 | 80.00000 |
| College graduate or above | 1 | 51.21154 |
`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by Education and Gender.
ℹ Output is grouped by Education.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(Education, Gender))` for per-operation grouping
(`?dplyr::dplyr_by`) instead.
# A tibble: 14 × 3
# Groups: Education [7]
Education Gender avg_age
<chr> <int> <dbl>
1 9-11th grade 1 53.0
2 9-11th grade 2 54.1
3 College graduate or above 1 51.2
4 College graduate or above 2 48.1
5 Don't Know 1 80
6 High school graduate/GED or equivalent 1 51.9
7 High school graduate/GED or equivalent 2 53.4
8 Less than 9th grade 1 56.6
9 Less than 9th grade 2 59.8
10 Some college or AA degree 1 51.4
11 Some college or AA degree 2 52.7
12 <NA> 1 17.5
13 <NA> 2 17.4
14 <NA> NA NaN
When we use GROUP BY, we are defining the units of aggregation. A common misconception is that grouping by multiple columns creates separate groups for each column independently. In reality, grouping uses the combination of the columns to define each group. In this example,
Education) creates one group for each unique Education level.Education and Gender) creates one group for each unique pair of values.As a result, the same Education level can appear in multiple rows because each row represents a different combination with the second grouping variable. For example:
Some college or AA degree + 1Some college or AA degree + 2both belong to the same Education category, but they are different groups because the Gender value changes.
Similar to WHERE, HAVING allows us to filter rows in the table after the GROUP BY clause. Using WHERE after GROUP BY is invalid.
For instance, we only want the average age of observations with College graduate or above education.
If you want to filter rows such that the variable value is in a list of values, we can use IN and (your_list_of_values).
For instance, we only want to calculate the average ages of observations with College graduate or above, Some college or AA degree, and 9-11th grade respectively.
# A tibble: 3 × 2
Education avg_age
<chr> <dbl>
1 9-11th grade 53.5
2 College graduate or above 49.7
3 Some college or AA degree 52.1
SQL provides several set operations that allow you to combine results of two queries (or two datasets) on row level.
Note that an operand to a set operator must be a complete query. For instance, if we wanted to conduct SET_OPERATION on tables nhanes_set1 and nhanes_set2, we couldn’t just write (nhanes_set1) SET_OPERATION (nhanes_set2).
UNION returns all rows that appear in either of the two result sets. In the example below, we have nhanes_set1 containing information from participants with ID 93731, 93801,and 93823; and nhanes_set2 containing information from participant with ID 93823, 93830, and 93840. The result outputs 5 rows in total. Notice that participant 93823 appears twice since it exists on both nhanes_set1 and nhanes_set2. UNION automatically eliminates duplicates.
| SEQN | Age | Gender | Ethnicity | Citizenship | Education | Marital_status | Household_size | Annual_income | Sedentary_min | Height | Weight | Health_insurance | Private_insurance | Cancer | Heart_attack | Stroke | Inc_exercise | Dec_salt | Dec_fat | LDL_Cholesterol | Triglyceride |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 93840 | 47 | 1 | non-Hispanic Asian | Citizen by birth or naturalization | College graduate or above | Married | 3 | $100000 and Over | 600 | 70 | 172 | Yes | Yes | No | No | No | Yes | No | No | 104 | 52 |
| 93731 | 20 | 1 | Mexican American | Citizen by birth or naturalization | Some college or AA degree | Never married | 5 | $100000 and Over | 360 | 73 | 202 | Yes | Yes | No | No | No | No | No | Yes | 97 | 78 |
| 93830 | 67 | 1 | non-Hispanic black | Citizen by birth or naturalization | Some college or AA degree | Married | 2 | $75000 to $99999 | 360 | 75 | 252 | Yes | NA | No | No | Yes | Yes | Yes | Yes | 81 | 55 |
| 93801 | 59 | 1 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | $100000 and Over | 600 | 71 | 175 | Yes | NA | No | No | No | No | Yes | Yes | 94 | 136 |
| 93823 | 58 | 2 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | Under $20000 | 180 | 64 | 170 | No | NA | No | No | No | Yes | No | Yes | 156 | 128 |
SEQN Age Gender Ethnicity Citizenship
1 93731 20 1 Mexican American Citizen by birth or naturalization
2 93801 59 1 Other Hispanic Citizen by birth or naturalization
3 93823 58 2 Other Hispanic Citizen by birth or naturalization
4 93830 67 1 non-Hispanic black Citizen by birth or naturalization
5 93840 47 1 non-Hispanic Asian Citizen by birth or naturalization
Education Marital_status Household_size Annual_income
1 Some college or AA degree Never married 5 $100000 and Over
2 College graduate or above Married 2 $100000 and Over
3 College graduate or above Married 2 Under $20000
4 Some college or AA degree Married 2 $75000 to $99999
5 College graduate or above Married 3 $100000 and Over
Sedentary_min Height Weight Health_insurance Private_insurance Cancer
1 360 73 202 Yes Yes No
2 600 71 175 Yes <NA> No
3 180 64 170 No <NA> No
4 360 75 252 Yes <NA> No
5 600 70 172 Yes Yes No
Heart_attack Stroke Inc_exercise Dec_salt Dec_fat LDL_Cholesterol
1 No No No No Yes 97
2 No No No Yes Yes 94
3 No No Yes No Yes 156
4 No Yes Yes Yes Yes 81
5 No No Yes No No 104
Triglyceride
1 78
2 136
3 128
4 55
5 52
To keep the duplicates, we can use UNION ALL. The result outputs 6 rows in total.
| SEQN | Age | Gender | Ethnicity | Citizenship | Education | Marital_status | Household_size | Annual_income | Sedentary_min | Height | Weight | Health_insurance | Private_insurance | Cancer | Heart_attack | Stroke | Inc_exercise | Dec_salt | Dec_fat | LDL_Cholesterol | Triglyceride |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 93731 | 20 | 1 | Mexican American | Citizen by birth or naturalization | Some college or AA degree | Never married | 5 | $100000 and Over | 360 | 73 | 202 | Yes | Yes | No | No | No | No | No | Yes | 97 | 78 |
| 93801 | 59 | 1 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | $100000 and Over | 600 | 71 | 175 | Yes | NA | No | No | No | No | Yes | Yes | 94 | 136 |
| 93823 | 58 | 2 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | Under $20000 | 180 | 64 | 170 | No | NA | No | No | No | Yes | No | Yes | 156 | 128 |
| 93823 | 58 | 2 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | Under $20000 | 180 | 64 | 170 | No | NA | No | No | No | Yes | No | Yes | 156 | 128 |
| 93830 | 67 | 1 | non-Hispanic black | Citizen by birth or naturalization | Some college or AA degree | Married | 2 | $75000 to $99999 | 360 | 75 | 252 | Yes | NA | No | No | Yes | Yes | Yes | Yes | 81 | 55 |
| 93840 | 47 | 1 | non-Hispanic Asian | Citizen by birth or naturalization | College graduate or above | Married | 3 | $100000 and Over | 600 | 70 | 172 | Yes | Yes | No | No | No | Yes | No | No | 104 | 52 |
SEQN Age Gender Ethnicity Citizenship
1 93731 20 1 Mexican American Citizen by birth or naturalization
2 93801 59 1 Other Hispanic Citizen by birth or naturalization
3 93823 58 2 Other Hispanic Citizen by birth or naturalization
4 93823 58 2 Other Hispanic Citizen by birth or naturalization
5 93830 67 1 non-Hispanic black Citizen by birth or naturalization
6 93840 47 1 non-Hispanic Asian Citizen by birth or naturalization
Education Marital_status Household_size Annual_income
1 Some college or AA degree Never married 5 $100000 and Over
2 College graduate or above Married 2 $100000 and Over
3 College graduate or above Married 2 Under $20000
4 College graduate or above Married 2 Under $20000
5 Some college or AA degree Married 2 $75000 to $99999
6 College graduate or above Married 3 $100000 and Over
Sedentary_min Height Weight Health_insurance Private_insurance Cancer
1 360 73 202 Yes Yes No
2 600 71 175 Yes <NA> No
3 180 64 170 No <NA> No
4 180 64 170 No <NA> No
5 360 75 252 Yes <NA> No
6 600 70 172 Yes Yes No
Heart_attack Stroke Inc_exercise Dec_salt Dec_fat LDL_Cholesterol
1 No No No No Yes 97
2 No No No Yes Yes 94
3 No No Yes No Yes 156
4 No No Yes No Yes 156
5 No Yes Yes Yes Yes 81
6 No No Yes No No 104
Triglyceride
1 78
2 136
3 128
4 128
5 55
6 52
INTERSECT returns the rows that appear in both sets. Using the same nhanes_set1 and nhanes_set2, the result only outputs the row corresponding to participant 93823.
| SEQN | Age | Gender | Ethnicity | Citizenship | Education | Marital_status | Household_size | Annual_income | Sedentary_min | Height | Weight | Health_insurance | Private_insurance | Cancer | Heart_attack | Stroke | Inc_exercise | Dec_salt | Dec_fat | LDL_Cholesterol | Triglyceride |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 93823 | 58 | 2 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | Under $20000 | 180 | 64 | 170 | No | NA | No | No | No | Yes | No | Yes | 156 | 128 |
SEQN Age Gender Ethnicity Citizenship
1 93823 58 2 Other Hispanic Citizen by birth or naturalization
Education Marital_status Household_size Annual_income
1 College graduate or above Married 2 Under $20000
Sedentary_min Height Weight Health_insurance Private_insurance Cancer
1 180 64 170 No <NA> No
Heart_attack Stroke Inc_exercise Dec_salt Dec_fat LDL_Cholesterol
1 No No Yes No Yes 156
Triglyceride
1 128
EXCEPT returns rows from the first set (the one on the FROM query) that do not appear in the second.
| SEQN | Age | Gender | Ethnicity | Citizenship | Education | Marital_status | Household_size | Annual_income | Sedentary_min | Height | Weight | Health_insurance | Private_insurance | Cancer | Heart_attack | Stroke | Inc_exercise | Dec_salt | Dec_fat | LDL_Cholesterol | Triglyceride |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 93801 | 59 | 1 | Other Hispanic | Citizen by birth or naturalization | College graduate or above | Married | 2 | $100000 and Over | 600 | 71 | 175 | Yes | NA | No | No | No | No | Yes | Yes | 94 | 136 |
| 93731 | 20 | 1 | Mexican American | Citizen by birth or naturalization | Some college or AA degree | Never married | 5 | $100000 and Over | 360 | 73 | 202 | Yes | Yes | No | No | No | No | No | Yes | 97 | 78 |
SEQN Age Gender Ethnicity Citizenship
1 93731 20 1 Mexican American Citizen by birth or naturalization
2 93801 59 1 Other Hispanic Citizen by birth or naturalization
Education Marital_status Household_size Annual_income
1 Some college or AA degree Never married 5 $100000 and Over
2 College graduate or above Married 2 $100000 and Over
Sedentary_min Height Weight Health_insurance Private_insurance Cancer
1 360 73 202 Yes Yes No
2 600 71 175 Yes <NA> No
Heart_attack Stroke Inc_exercise Dec_salt Dec_fat LDL_Cholesterol
1 No No No No Yes 97
2 No No No Yes Yes 94
Triglyceride
1 78
2 136
Note that EXCEPT removes all occurrences of duplicate data from the first set.
If you wish to removes one occurrence of duplicate data from the first set for every occurrence in the second set, use EXCEPT ALL.
We can merge datasets using JOIN if they share common columns. The general syntax is A JOIN_TYPE B, where JOIN_TYPE depends on the join operation we want to perform. This syntax would perform the join based on all shared attributes (columns) between A and B. If the shared attributes you wish to merge on are named differently in A and B, we can use ON to specify the condition. For instance, A JOIN_TYPE B ON A.colname1 = B.colname2 will join rows from A and B by matching values between colname1 from A and colname2 from B.
In this section, we will use the following datasets.
nhanes_demographics includes ID, Age, Gender and Education level of 5 patients.
| SEQN | Age | Gender | Education |
|---|---|---|---|
| 93731 | 20 | 1 | Some college or AA degree |
| 93840 | 47 | 1 | College graduate or above |
| 93920 | 61 | 2 | College graduate or above |
| 93972 | 35 | 1 | Some college or AA degree |
| 94069 | 61 | 1 | High school graduate/GED or equivalent |
nhanes_health includes ID, Heart attack status, and Stroke Status of 6 patients.
| SEQN | Heart_attack | Stroke |
|---|---|---|
| 93731 | No | No |
| 93840 | No | No |
| 93920 | No | No |
| 102838 | No | No |
| 102880 | No | No |
| 102947 | No | No |
Note that ID 93731, 93840, and 93920 appear on both table. ID 93972 and 94069 only appear in nhanes_demographics and ID 102838, 102880, and 102947 only appear in nhanes_health.
Suppose we are only interested in patients’ age and their stroke status, there are 4 main types of joins:
An inner natural join:
will include only IDs that are in the intersection of nhanes_demographics and nhanes_health.
A full outer join:
will include all IDs that are in either nhanes_demographics or nhanes_health. For patients that appear in nhanes_demographics, but not in nhanes_health (i.e., 93972 and 94069), null values will be inserted to Stroke (column in nhanes_health). Similarly, For patients that appear in nhanes_health, but not in nhanes_demographics (i.e., 102838, 102880, and 102947), null values will be inserted to Age (column in nhanes_demographics).
A left outer join:
will include all IDs that are in the intersection (i.e., 93731, 93840, and 93920) plus those that are in nhanes_demographics only (i.e., 93972 and 94069) with null values inserted to Stroke (column in nhanes_health).
A right outer join:
will include all IDs that are in the intersection (i.e., 93731, 93840, and 93920) plus those that are in nhanes_health only (i.e., 102838, 102880, and 102947) with null values inserted to Age (column in nhanes_demographics).