2 Should group by wp_posts.ID as tr.object_id may be null.
source | link
function wpse155797_posts_clauses( $pieces, $query ) {
    if ( ! is_admin() || ! $query->is_main_query() ) {
        return $pieces;
    }
    global $wpdb;
    if ( ( $orderby = $query->get( 'orderby' ) ) == 'asset_type' ) {
        if ( ( $order = strtoupper( $query->get( 'order' ) ) ) != 'DESC' ) $order = 'ASC';
        $pieces[ 'join' ] .= ' LEFT JOIN ' . $wpdb->term_relationships . ' AS tr ON ' . $wpdb->posts . '.ID = tr.object_id'
            . ' LEFT JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id'
            . ' LEFT JOIN ' . $wpdb->terms . ' AS t ON tt.term_id = t.term_id';
        $pieces[ 'fields' ] .= ', group_concat(t.name ORDER BY t.name ' . $order . ') AS ' . $orderby;
        $pieces[ 'groupby' ] = 'tr$wpdb->posts .object_id'; '.ID';
        $pieces[ 'orderby' ] = $orderby . ' ' . $order . ', ' . $wpdb->posts . '.post_title ASC';
    }
    return $pieces;
}
add_filter( 'posts_clauses', 'wpse155797_posts_clauses', 10, 2 );
function wpse155797_posts_clauses( $pieces, $query ) {
    if ( ! is_admin() || ! $query->is_main_query() ) {
        return $pieces;
    }
    global $wpdb;
    if ( ( $orderby = $query->get( 'orderby' ) ) == 'asset_type' ) {
        if ( ( $order = strtoupper( $query->get( 'order' ) ) ) != 'DESC' ) $order = 'ASC';
        $pieces[ 'join' ] .= ' LEFT JOIN ' . $wpdb->term_relationships . ' AS tr ON ' . $wpdb->posts . '.ID = tr.object_id'
            . ' LEFT JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id'
            . ' LEFT JOIN ' . $wpdb->terms . ' AS t ON tt.term_id = t.term_id';
        $pieces[ 'fields' ] .= ', group_concat(t.name ORDER BY t.name ' . $order . ') AS ' . $orderby;
        $pieces[ 'groupby' ] = 'tr.object_id';
        $pieces[ 'orderby' ] = $orderby . ' ' . $order . ', ' . $wpdb->posts . '.post_title ASC';
    }
    return $pieces;
}
add_filter( 'posts_clauses', 'wpse155797_posts_clauses', 10, 2 );
function wpse155797_posts_clauses( $pieces, $query ) {
    if ( ! is_admin() || ! $query->is_main_query() ) {
        return $pieces;
    }
    global $wpdb;
    if ( ( $orderby = $query->get( 'orderby' ) ) == 'asset_type' ) {
        if ( ( $order = strtoupper( $query->get( 'order' ) ) ) != 'DESC' ) $order = 'ASC';
        $pieces[ 'join' ] .= ' LEFT JOIN ' . $wpdb->term_relationships . ' AS tr ON ' . $wpdb->posts . '.ID = tr.object_id'
            . ' LEFT JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id'
            . ' LEFT JOIN ' . $wpdb->terms . ' AS t ON tt.term_id = t.term_id';
        $pieces[ 'fields' ] .= ', group_concat(t.name ORDER BY t.name ' . $order . ') AS ' . $orderby;
        $pieces[ 'groupby' ] = $wpdb->posts . '.ID';
        $pieces[ 'orderby' ] = $orderby . ' ' . $order . ', ' . $wpdb->posts . '.post_title ASC';
    }
    return $pieces;
}
add_filter( 'posts_clauses', 'wpse155797_posts_clauses', 10, 2 );
1
source | link

You could use the posts_clauses filter:

function wpse155797_posts_clauses( $pieces, $query ) {
    if ( ! is_admin() || ! $query->is_main_query() ) {
        return $pieces;
    }
    global $wpdb;
    if ( ( $orderby = $query->get( 'orderby' ) ) == 'asset_type' ) {
        if ( ( $order = strtoupper( $query->get( 'order' ) ) ) != 'DESC' ) $order = 'ASC';
        $pieces[ 'join' ] .= ' LEFT JOIN ' . $wpdb->term_relationships . ' AS tr ON ' . $wpdb->posts . '.ID = tr.object_id'
            . ' LEFT JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id'
            . ' LEFT JOIN ' . $wpdb->terms . ' AS t ON tt.term_id = t.term_id';
        $pieces[ 'fields' ] .= ', group_concat(t.name ORDER BY t.name ' . $order . ') AS ' . $orderby;
        $pieces[ 'groupby' ] = 'tr.object_id';
        $pieces[ 'orderby' ] = $orderby . ' ' . $order . ', ' . $wpdb->posts . '.post_title ASC';
    }
    return $pieces;
}
add_filter( 'posts_clauses', 'wpse155797_posts_clauses', 10, 2 );

This assumes you've added

$columns['taxonomy-asset_type'] = 'asset_type';

to your manage_edit_sortable_columns filter.

This sorts by taxonomy terms but looks funny if you sort descending and have more than one term as each group still lists alphabetically. To fix this you need to override the default 'taxonomy-asset_type' column in your manage_posts_columns filter to be eg 'asset_type' (changing the manage_edit_sortable_columns column name above to match) and then duplicate the standard WP output code in your manage_posts_custom_column action with an added array_reverse on the output:

    case 'asset_type':
        $taxonomy = 'asset_type';
        if ( $terms = get_the_terms( $post->ID, $taxonomy ) ) {
            $out = array();
            foreach ( $terms as $t ) {
                $posts_in_term_qv = array();
                $posts_in_term_qv['taxonomy'] = $taxonomy;
                $posts_in_term_qv['term'] = $t->slug;

                $out[] = sprintf( '<a href="%s">%s</a>',
                    esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ),
                    esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) )
                );
            }
            // New bit
            if ( isset( $_GET['order'] ) && $_GET['order'] == 'desc' ) {
                $out = array_reverse( $out );
            }
            /* translators: used between list items, there is a space after the comma */
            echo join( __( ', ' ), $out );
        } else {
            echo '&#8212;';
        }
        break;